HDU 1312 Red and Black (分别用并查集和DFS实现)



http://acm.hdu.edu.cn/showproblem.php?pid=1312

Red and Black

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 10078    Accepted Submission(s): 6282


Problem Description
There is a rectangular room, covered with square tiles. Each tile is colored either red or black. A man is standing on a black tile. From a tile, he can move to one of four adjacent tiles. But he can't move on red tiles, he can move only on black tiles.

Write a program to count the number of black tiles which he can reach by repeating the moves described above.
 


Input
The input consists of multiple data sets. A data set starts with a line containing two positive integers W and H; W and H are the numbers of tiles in the x- and y- directions, respectively. W and H are not more than 20.

There are H more lines in the data set, each of which includes W characters. Each character represents the color of a tile as follows.

'.' - a black tile
'#' - a red tile
'@' - a man on a black tile(appears exactly once in a data set)
 


Output
For each data set, your program should output a line which contains the number of tiles he can reach from the initial tile (including itself).
 


Sample Input
  
  
6 9 ....#. .....# ...... ...... ...... ...... ...... #@...# .#..#. 11 9 .#......... .#.#######. .#.#.....#. .#.#.###.#. .#.#..@#.#. .#.#####.#. .#.......#. .#########. ........... 11 6 ..#..#..#.. ..#..#..#.. ..#..#..### ..#..#..#@. ..#..#..#.. ..#..#..#.. 7 7 ..#.#.. ..#.#.. ###.### ...@... ###.### ..#.#.. ..#.#.. 0 0
 


Sample Output
  
  
45 59 6 13
 

第一次写是用的是并查集,因为还不会DFS。用并查集时想不到怎样用二维数组,所以只能用一维数组,用行和列的关系表示全部数据,为了使用并查集转换成一维数组。而且写完以后,运行第二组数据不对,因为没有把全部可连的路连起来,所以又加了一次合并,才正确,个人感觉,写这种题用并查集不稳定,或者说不保险,因为如果遇到更特殊数据那就不是加一次合并的问题了没准要进行N次,虽然N是有限整数,但一定会超时)。

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define MAX 10000
struct SET
{
    int S;//按照顺序是第几个数,从0开始
    char J;//存放'.','@','#'
}Set[MAX+10];
int Find(int x)
{
    int r=x,i=x,j;
    while(Set[r].S!=r)
    r=Set[r].S;

    while(i!=r)
    {//路径压缩 ,很关键 
        j=Set[i].S;
        Set[i].S=r;
        i=j;
    }
    return r;
}
void Merge(int x,int y)
{//合并 
    x=Find(x);
    y=Find(y);
    if(y<x)
    Set[x].S=y;
    if(x<y)
    Set[y].S=x;
}
int main()
{
    int i,j,k,T,M,N,sum,num,I,J;
    while(scanf("%d %d",&N,&M)&&(N!=0||M!=0))
    {
    	getchar();
        for(i=0;i<M;i++)//给Set[].S赋值0~M*N-1
        {
		    for(j=0;j<N;j++)
            {
                num=N*i+j;
                scanf("%c",&Set[num].J);
                if(Set[num].J=='@')
                {
                	I=i;
                	J=j;
                }
                Set[num].S=num;
            }
            getchar();
        }
        for(i=0;i<M;i++)
        {
	       for(j=0;j<N;j++)
           {//每一次都执行下边四个if,保证指向最小的数(通过并查集把联通的点指向最小的那个数)
                num=N*i+j;
                //下面分别搜索上下左右,如果是'.'或'@'的话就合并
                if((Set[num].J=='.'||Set[num].J=='@')&&i-1>=0&&(Set[N*(i-1)+j].J=='.'||Set[N*(i-1)+j].J=='@'))//上
                Merge(num,N*(i-1)+j);
				   
                if((Set[num].J=='.'||Set[num].J=='@')&&i+1<M&&(Set[N*(i+1)+j].J=='.'||Set[N*(i+1)+j].J=='@'))//下
                Merge(num,N*(i+1)+j);

                if((Set[num].J=='.'||Set[num].J=='@')&&j-1>=0&&(Set[N*i+j-1].J=='.'||Set[N*i+j-1].J=='@'))//左
                Merge(num,N*i+j-1);

                if((Set[num].J=='.'||Set[num].J=='@')&&j+1<N&&(Set[N*i+j+1].J=='.'||Set[N*i+j+1].J=='@'))//右
                Merge(num,N*i+j+1);        
		   }
       }
       for(i=0;i<M;i++)
       {
	       for(j=0;j<N;j++)
           {//每一次都执行下边四个if,保证指向最小的数(通过并查集把联通的点指向最小的那个数)
                num=N*i+j;
                //下面分别搜索上下左右,如果是'.'或'@'的话就合并
                if((Set[num].J=='.'||Set[num].J=='@')&&i-1>=0&&(Set[N*(i-1)+j].J=='.'||Set[N*(i-1)+j].J=='@'))//上
                Merge(num,N*(i-1)+j);
				   
                if((Set[num].J=='.'||Set[num].J=='@')&&i+1<M&&(Set[N*(i+1)+j].J=='.'||Set[N*(i+1)+j].J=='@'))//下
                Merge(num,N*(i+1)+j);

                if((Set[num].J=='.'||Set[num].J=='@')&&j-1>=0&&(Set[N*i+j-1].J=='.'||Set[N*i+j-1].J=='@'))//左
                Merge(num,N*i+j-1);

                if((Set[num].J=='.'||Set[num].J=='@')&&j+1<N&&(Set[N*i+j+1].J=='.'||Set[N*i+j+1].J=='@'))//右
                Merge(num,N*i+j+1);        
		   }
       }
       //要进行两次合并,因为一次不一定能把应该在一起的合并到一起 
       
        for(i=0,sum=0;i<M*N;i++)//查看有几个与I,J对应的头相同 
        if(Set[i].S==Set[N*I+J].S)
        sum+=1;

        printf("%d\n",sum);
    }
    return 0;
}

第二次用DFS写的,显然简洁明了

#include<stdio.h>
#include<string.h>
char map[30][30];//存放'.','@','#' 
bool judge[30][30];//标记是否走过 
int sum;//可走路的大小 
int x,y;
int M,N;//M行,N列 
void DFS(int X,int Y)
{
	int i,j;//这里的变量不能为全局变量 
	judge[X][Y]=true;//标记已走 
	for(i=-1;i<=1;i++)
	{
	  for(j=-1;j<=1;j++)
	  {//分别对左,下,上,右进行DFS 
		    
			if(!(i==-1&&j==0||i==0&&j==-1||i==0&&j==1||i==1&&j==0))//排除除上下左右以外的其他所有组合
			continue;
			x=X+j;
			y=Y+i;
			if(x>=0&&x<=N-1&&y>=0&&y<=M-1&&!judge[x][y]&&map[x][y]!='#')
			{//不能越界,不能走'#',不走已标记的 
			   sum+=1;
			   DFS(x,y);
		    }
	  }
    }
}
int main()
{
	int i,j,I,J;//I,J为起点位置 
	while(scanf("%d %d",&M,&N)&&!(M==0&&N==0))
	{
		memset(judge,false,sizeof(judge));
		getchar();
		for(i=0;i<N;i++)
		{
		   for(j=0;j<M;j++)
		   {
		      scanf("%c",&map[i][j]);
			  if(map[i][j]=='@')
			  {
			      I=i;
				  J=j;
			  }
		   }
		   getchar();
	    }
	    sum=1;
	    DFS(I,J);
	    printf("%d\n",sum);
	}
	return 0;
}
	    
		










  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值