1. 程式人生 > >DFS模板以及例項——油田(用DFS求連通塊)

DFS模板以及例項——油田(用DFS求連通塊)

首先是DFS模板套路: 

void DFS(...)//v是頂點
{
    訪問結點相關操作;
    for(從結點的第一個相鄰接點;結點沒有相鄰接點了即終止條件;下一個相鄰結點)
    {
        if(相鄰結點滿足條件如未訪問過)
        {
            對相鄰結點呼叫DFS();
        }
    }
}   
int main()
{
    初始化是否訪問標記;
    for(int v=xxx;v<Vnum;v++){//xxx表示從第xxx個頂點開始遍歷,第xxx個結點是一個連通分量的任一結點。
                              //呼叫一次DFS為訪問了圖的一個連通分量的所有結點
                              //若已知圖為連通的,則不需for迴圈,直接呼叫DFS()即可
        if(滿足條件如為訪問過)
        {
            呼叫DFS();
        }
    }
    return 0;
}

例題:

油田(Oil Deposits, UVa 572,ZOJ1709,POJ1562)
輸入一個m行n列的字元矩陣,統計字元“@”組成多少個八連塊。如果兩個字元“@”所在的格子相鄰(橫、豎或者對角線方向),就說它們屬於同一個八連塊。例如,下圖中有兩個八連塊。

輸入描述:

輸入檔案中包含多個測試資料,每個測試資料描述了一個網格。每個網格資料的第1行為兩個整數:m、n,分別表示網格的行和列;如果m=0,則表示輸入結束,否則1<=m<=100,1<=n<=100。接下來有m行資料,每行資料有n個字元(不包括行結束符)。每個字元代表一個小方塊,如果為“*”,則代表沒有石油,如果為“@”,則代表有石油,是一個pocket。

輸出描述:

對輸入檔案中的每個網格,輸出網格中不同的油田數目。如果兩塊不同的pocket在水平、垂直或者對角線方向上相鄰,則被認為屬於同一塊油田。每塊油田所包含的pocket數目不會超過100。

樣例輸入:

5 5

****@

*@@*@

*@**@

@@@*@

@@**@

樣例輸出:

2

本題程式碼: 

/*注意要求多組輸入*/ 
#include<iostream>
#include<cstring>//memset在這個標頭檔案裡 
using namespace std;
#define max 100+5
char a[max][max];//油田
int visited[max][max]; 
int m,n;
//現在位置x,y 
void dfs(int x,int y)
{
	visited[x][y]=1;//將現在的位置變為已訪問過 
	for(int i=-1;i<=1;i++)
	{
		for(int j=-1;j<=1;j++)
		{//迴圈遍歷移動的八個方向 
			int nx=x+i,ny=y+j;//向x方向移動i,向y方向移動j 
			if(nx>=0&&nx<=m-1&&ny>=0&&ny<=n-1&&visited[nx][ny]==0&&a[nx][ny]=='@')//若下一個位置在園子內並且沒被訪問過並且是油田 
			{
				dfs(nx,ny);
			}
		}
	}
	return;
}
int main()
{
	while(cin>>m>>n)
	{
		int res=0;//進行dfs的次數,即為圖的連通分量的個數 
		if(m==0)
			break;
		memset(visited,0,sizeof(visited));//將visited陣列全初始化為0,表示沒訪問過;1表示已訪問過 
		for(int i=0;i<m;i++)
		{
			for(int j=0;j<n;j++)
			{
				cin>>a[i][j];
			}
		}
		for(int i=0;i<m;i++)
		{
			for(int j=0;j<n;j++)//從有油田@開始dfs 
			{
				if(visited[i][j]==0&&a[i][j]=='@')
				{
					dfs(i,j);
					res++;
				}
			}
		}
		cout<<res<<endl; 
	}
	return 0;
}

另一種DFS模板套路:

 void dfs(int step)
 {
      if(邊界成立)
      {
          。。。。(相關操作)
          return;
      }
      for(嘗試每一種可能)
      {
         把這種可能標記表示走過
         。。。。(相關操作)

         繼續下一步dfs(step+1);
         把這種可能標記去除
      }
      return;
 }

本題另一種程式碼:

#include<iostream>
#include<cstring>
using namespace std;
#define max 1000+5
int m,n;
int visited[max][max];
char a[max][max];
void dfs(int x,int y)
{
	if(x<0||x>=m||y<0||y>=n||visited[x][y]==1||a[x][y]!='@')
		return;
	visited[x][y]=1;
	for(int i=-1;i<=1;i++)
	{
		for(int j=-1;j<=1;j++)
		{
			int nx=x+i,ny=y+j;
			dfs(nx,ny);
		}
	}
}
int main()
{
	while(cin>>m>>n)
	{
		if(m==0)
			break;
		int res=0;
		memset(visited,0,sizeof(visited));//將visited陣列全初始化為0,表示沒訪問過;1表示已訪問過.一定不要忘記初始化!!!因為是全域性變數,每一次一定要把visited陣列都初始化為0且a陣列以及m,n重新輸入的
		for(int i=0;i<m;i++)
		{
			for(int j=0;j<n;j++)
			{
				cin>>a[i][j];
			} 
		}
		for(int i=0;i<m;i++)
		{
			for(int j=0;j<n;j++)
			{
				if(visited[i][j]==0&&a[i][j]=='@')
				{
					dfs(i,j);
					res++;
				}
			} 
		}
		cout<<res<<endl; 
	}
	return 0;
}