小白今天也學習了一道dfs的題,想記錄下來,嘿嘿嘿
棋盤問題
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 85425 Accepted: 39462
Description
在一個給定形狀的棋盤(形狀可能是不規則的)上面擺放棋子,棋子沒有區別。要求擺放時任意的兩個棋子不能放在棋盤中的同一行或者同一列,請編程求解對於給定形狀和大小的棋盤,擺放k個棋子的所有可行的擺放方案C。
Input
輸入含有多組測試數據。
每組數據的第一行是兩個正整數,n k,用一個空格隔開,表示了將在一個n*n的矩陣內描述棋盤,以及擺放棋子的數目。 n <= 8 , k <= n
當爲-1 -1時表示輸入結束。
隨後的n行描述了棋盤的形狀:每行有n個字符,其中 # 表示棋盤區域, . 表示空白區域(數據保證不出現多餘的空白行或者空白列)。
Output
對於每一組數據,給出一行輸出,輸出擺放的方案數目C (數據保證C<2^31)。
Sample Input
2 1
#.
.#
4 4
…#
…#.
.#…
#…
-1 -1
Sample Output
2
1
小白剛剛接觸DFS,還不是很理解這個東西,感覺一層一層遞歸好神奇 ,還是需要多理解,多練習啊!DFS的代碼比BFS短,但是不容易理解,還是得多做題,恩。
#include <iostream>
#include <string.h>
using namespace std;
char maps[9][9];//讀取棋盤
int lie[9],ans=0,k,n,m=0;
void dfs(int row)
{
if(row>n)//超出棋盤範圍則返回
return;
if(m==k)//當搜索長度與要求的相同時,返回
{
ans++;
return;
}
for(int i=0;i<n;i++)
{
if(maps[row][i]=='#'&&lie[i]==0)
{
lie[i]=1;
m++;
dfs(row+1);
m--;//恢復之前的狀態
lie[i]=0;
}
}
dfs(row+1);//下一行
}
int main()
{
while(cin>>n>>k&&n!=-1)
{
ans=0;m=0;
memset(maps,0,sizeof(maps));
memset(lie,0,sizeof(lie));
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
cin>>maps[i][j];
}
dfs(0);
cout<<ans<<endl;
}
}
恢復狀態那裏需要多加理解,它是遞歸返回之後接着找該層合適的點,那麼下一層自然要恢復成沒有被訪問過的狀態。
hdoj 2553 N皇后問題
題目大意: 此題是比較簡單的N皇后問題,即在10以內的N皇后問題,在棋盤上放置n個皇后,使他們不同行,不同列,不同對角線,詢問有多少個放置方法。
題解: 此題需要用到dfs搜索,重要的是 回溯與剪枝 ,即去除不符合要求的放置方法。首先要打表,如果不打表會超時,然後運用dfs搜索,然後check函數判斷是否符合要求,這裏放在不同對角線,從(i,j)向斜對角走a步,新座標(r,c)有四種情況:(i-a,j-a),(i-a,j+a),(i+a,j-a),(i+a,j+a)。總結起來就是,|i-r|=|j-c|
AC代碼:
#include <iostream>
#include <string.h>
#include <cmath>
using namespace std;
int col[12]={0};
int ans[12]={0};
int n,sum=0;
bool check(int c,int r)
{
for(int i=0;i<r;i++)//從每一排檢查該列有沒有已經放下的
{
if(col[i]==c||abs(col[i]-c)==abs(i-r))
return false;
}
return true;
}
void dfs(int r)
{
if(r==n)
{
sum++;
return;
}
for(int i=0;i<n;i++)//每一列
{
if(check(i,r))
{
col[r]=i;
dfs(r+1);
}
}
}
int main()
{
for(n=0;n<=10;n++)//先打表
{
memset(col,0,sizeof(col));
sum=0;
dfs(0);//從第0排開始搜索
ans[n]=sum;
}
while(cin>>n&&n)
{
cout<<ans[n]<<endl;
}
}
hdoj 5113 Black And White
(此題大部分是自己寫的emm,但是在剪枝的時候看的dalao的代碼)
題目大意: 塗顏色問題,相鄰兩塊區域顏色不能相同(即共享某一條邊的區域),並且每種顏色指定出現次數,且各種顏色出現次數相加之和爲總塊數。寫出一個符合題意的塗色方法。
題解: 畢竟第一次自己寫dfs,中間出現了很多小錯誤。這裏建議在寫maps數組時,下標從1開始,在判斷是否可以在這個區域放某種顏色時比較方便。整體思路就是用dfs搜索,從(1,1)開始,然後依次往下,先判斷再塗顏色,如果此方案不可行,則遞歸會回退。如果搜索到最後此方案可行,則需要再在回退時加上return 1。
在剪枝時,如果剩下塊數的一般小於某種顏色剩下的顏色數,則此方案一定不可行(看的dalao的代碼,據說自己畫畫就出來了)。
AC代碼:
#include <iostream>
#include <string.h>
using namespace std;
int maps[6][6];
int num[26];
int tmp[26];
int n,m,k;
int dfs(int r,int c)
{
int flag=0;
if(r==n+1)
{
return 1;
}
int sum=(n*m-(r-1)*m-c+2)/2; //剪枝
for(int i=1;i<=k;i++)
{
if(num[i]-tmp[i]>sum)
return 0;
}
for(int i=1;i<=k;i++)
{
if(tmp[i]>=num[i]||maps[r][c-1]==i||maps[r-1][c]==i)
continue;
maps[r][c]=i;
tmp[i]++;
if(c==m)
flag=dfs(r+1,1);
else
flag=dfs(r,c+1);
if(flag==1)//回退時,如果方案可行,則需要返回1
return 1;
tmp[i]--; //不要忘記回到之前的狀態
maps[r][c]=0;
}
return 0;
}
int main()
{
int w,t=0;
cin>>w;
while(w--)
{
memset(maps,0,sizeof(maps));
memset(tmp,0,sizeof(tmp));
t++;
cin>>n>>m>>k;
for(int i=1;i<=k;i++)
{
cin>>num[i];
}
cout<<"Case #"<<t<<":"<<endl;
int flag=dfs(1,1);
if(flag==0)
cout<<"NO"<<endl;
else
{
cout<<"YES"<<endl;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cout<<maps[i][j];
if(j==m)
cout<<endl;
else
cout<<" ";
}
}
}
}
}