首页 > 代码库 > UVa 657 掷色子

UVa 657 掷色子

题意:就是有一张大图,每个像素即格子只可能是 . * X 三种,分别代表背景、色子、色子的点数。两个格子是相邻的或连通的,当且仅当两个格子是*或X,且有公共边,即上下左右四个方向,对角不算,即四连块。将一个连通块看做一个色子,将这个连通块中的X的连通块个数看做该色子的点数。
思路:两次深搜。第一次是由*和X来深搜每个连通块,在深搜每个连通块时由X来深搜X的连通块个数。这里可以通过两个标记数组visit来表示是否访问过,visitx来表示是否访问深搜X过。(也可以将X改为*、*改为.的方式实现,不用标记数组。之前我想的是用一个标记数组,然后对其值的判断来实现,不如两个标记数组来得简便和直观)

注意:在主函数的两个for循环进行深搜前,注意判断是否已访问过,否则产生大量点数为0的色子。另外,因为这个循环每次成功去深搜都是深搜了一个大的连通块,所以色子个数的增加应该在这个循环里,不要放在dfs里~

(之前的思路是,深搜一次,*在深搜时遇到X则点数加1,X深搜时遇到X则点数不加。发现对2行2列的数据*XXX就会出错,因为这样算一个,那个思路算出来的是两个。之后看了网上的思路,参考了一下。两次深搜,学习了~)

这里的深搜都是标记其被访问过,每次深搜都是标记完一个连通块。然后统计了访问的次数,即连通块的个数:色子数、色子的点数。(包括之前油田那题的深搜也是这样。)

inclusive 包含的,exclusive才是排他的~

Code:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define MAXN 55

int cmp_int(const void *_a, const void *_b);
void dfs(int hs,int ls);
void dfsx(int hs,int ls);

int w,h;
char G[MAXN][MAXN];
int visit[MAXN][MAXN];
int visitx[MAXN][MAXN];
int dir[][4]={{-1,1,0,0},{0,0,1,-1}};//LRDU,前x后y,前水平后竖直 
int num[MAXN*MAXN];//相应色子的点数 
int cnt;//色子个数 

int main()
{
 //freopen("657.in","r",stdin);
 //freopen("657.out","w",stdout);
 int thrw=1;
 while(scanf("%d%d",&w,&h) && w && h)
 {
  memset(G,'.',sizeof(G));
  memset(visit,0,sizeof(visit));
  memset(visitx,0,sizeof(visitx));
  for(int i=1;i<=h;++i)
  {
   getchar();
   for(int j=1;j<=w;++j)
   {
    G[i][j]=getchar();       
   }
  }
  cnt=0;
  memset(num,0,sizeof(num));
  for(int i=1;i<=h;++i)
   for(int j=1;j<=w;++j)
    if(G[i][j]!='.' && !visit[i][j])//注意这里的!visit条件,不然会打出很多点数为0的色子 
    {
     dfs(i,j);  
     cnt++;//色子数增加            //注意这个别放在dfs语句中了,想一想就理解了 
     //printf("%d %d\n",i,j);  
    }
  qsort(num,cnt,sizeof(num[0]),cmp_int);
  printf("Throw %d\n",thrw++);
  for(int i=0;i<cnt;++i)
   if(i==0) printf("%d",num[i]);
   else printf(" %d",num[i]);
  printf("\n\n");                          
 }
 return 0;   
}

int cmp_int(const void *_a, const void *_b)
{
 return *(int*)_a-*(int*)_b;   
}

void dfsx(int hs,int ls)
{//由X来遍历连通块 
 if(G[hs][ls]!='X' || visitx[hs][ls]) return ;
 visitx[hs][ls]=1;
 for(int i=0;i<4;++i)
 {
  int nh=hs+dir[1][i];
  int nl=ls+dir[0][i];
  dfsx(nh,nl);       
 }    
}

void dfs(int hs,int ls)
{//从一点出发,由*和X来遍历连通块 
 if(G[hs][ls]=='.' || visit[hs][ls]) return ;
 visit[hs][ls]=1;
 if(G[hs][ls]=='X' && !visitx[hs][ls])
 {
  dfsx(hs,ls);
  num[cnt]++;//点数增加                 
 }   
 //else if(G[hs][ls]=='*')            //注意这里别加这个判断条件。因为就算是字符X也需要进行下面这个循环的遍历 
  for(int i=0;i<4;++i)
  {
   int nh=hs+dir[1][i];
   int nl=ls+dir[0][i];
   dfs(nh,nl);       
  }    
}


UVa 657 掷色子