dfs题大杂烩
棋盘问题 POJ - 1321
和经典的八皇后问题一样. 给你一个棋盘,只有#区域可以放棋子,同时同一行和同一列只能有一个棋子. 问你放k个棋子有多少种方案.
很明显,这是搜索题.
因为每一行和每一列只能有一个棋子,所以我们可以从第k行一直到第n行枚举所有放棋子的情况,即我们从当前状态(当前行)dfs到第n行.然后符合添加的,我们就ans++.
dfs过程见代码.

#include <cstdio> #include <cstring> int n,k,c,way; // 检查当前棋盘区域是否可以放置棋子 bool isok(char vis[][16],int x,int y) { int i; for(i=0; i<n; ++i) if('V'==vis[x][i]) return false; for(i=0; i<n; ++i) if('V'==vis[i][y]) return false; return true; } void dfs(char vis[16][16],int cnt,int line) { int i,j; char mvis[16][16]; if(k==cnt){ c++; // 走到了末尾 方案加一 return ; }else if(line==n){ return ; }else if(n-line+cnt<k){ //"剪枝" return ; }else{ memcpy(mvis,vis,sizeof(mvis)); //注意sizeof的"陷阱",sizeof(指针)==4,所以不能传sizeof(vis) sizeof(数组名)==数组大小 for(i=line; i<n; ++i){ //注意i=line 而不是0 for(j=0; j<n; ++j){ if('#'==mvis[i][j] && isok(mvis,i,j)){ // 当前坐标是棋盘区域, 同时同一行同一列没有放置棋子 mvis[i][j] = 'V'; // 放棋子 dfs(mvis,cnt+1,i+1); mvis[i][j] = '#'; // 取消放棋子的状态, 继续枚举其他情况 } } } return ; } } int main() { int i,j; char map[16][16]; // freopen("D:\input.txt","r",stdin); while(scanf("%d%d",&n,&k) && (n!=-1 || k!=-1)){ for(i=0; i<n; ++i){ scanf("%s",map[i]); } c=0; dfs(map,0,0); printf("%d ",c); } return 0; }
Fliptile POJ - 3279
给你一个0和1组成的矩阵.你可以将某一格子阵翻转.但是你翻转一个格子,他上下左右的格子也会背翻转.
问你最后是否可以全部翻转成0
如果可以输出一个矩阵: 对应01矩阵翻转的次数.要求该答案矩阵的翻转次数最少,同时如果有多个解,输出字典序最小的一个.
如果不可以, 则输出IMPOSSIBLE
首先,我们分析一下, 我们发现,对于一个格子来说,翻转偶数次是没有意义的.翻转奇数次,才会改变状态.
因为对于一个格子,翻转偶数次是没有贡献的,翻转多个奇数次都等价于翻转一次,所以如果格子需要翻转,那么我们翻转一次就够了.
同时,
我们假定从第一行开始考虑, 如果要全部翻转成0, 那么我们当前行的状态将有由下一行同一列的格子翻转来决定.
所以我们不难知道,如果上一行的这个格子是1的话,那么我们当前格子就需要翻转一下. 一直到最后一行翻转完毕,我们最后再判断一下最后一行是否全都是0,就可以确定答案是否存在.
但是,我们这样并不能保证答案最优.. 我们不难发现这种做法依赖于第一行的初始状态. 因为最多只有十列,所以我们不妨把第一行的所有状态都枚举一下,然后都进行答案的查找.
(ps,枚举状态可以用dfs或者二进制. 而我用的dfs搜索过去.)

#include <cstdio> #include <cstring> using namespace std; int _m[24][24]; int _ct[24][24]; int _ans[24][24]; int tmp[24][24]; int n, m; bool ishave; int qstep; int qsz = 1; inline int fff(int val, int mack) { return val ^ (1<<mack); } void dfs(int now, int step) { int i, j; if (now > m) { // 注意,这里需要对矩阵进行操作,但是操作完成后,我们需要把还原原先的矩阵. // 所以我用了一个临时矩阵来保存. memcpy(_ct, _m, sizeof(_ct)); for (i=2; i<=n; ++i) { for (j=1; j<=m; ++j) { if (_m[i-1][j]) { _m[i][j] = !_m[i][j]; _m[i-1][j] = !_m[i-1][j]; _m[i+1][j] = !_m[i+1][j]; _m[i][j-1] = !_m[i][j-1]; _m[i][j+1] = !_m[i][j+1]; tmp[i][j] = 1; step++; } else tmp[i][j] = 0; } } for (i=1; i<=m; ++i) if (_m[n][i]) { // 当前状态不能全部翻转为0 memcpy(_m, _ct, sizeof(_m)); return ; } if (step > qstep) { // 步数判断 memcpy(_m, _ct, sizeof(_m)); return ; } qstep = step; for (i=1; i<=n; ++i) { // 字典序判断, 注意,这个题的字典序是从右到左的. for (j=m; j>=1; --j) { if (tmp[i][j] != _ans[i][j]) goto A; } } A: if (i<=n && tmp[i][j] < _ans[i][j]) { ishave = true; for (i=1; i<=n; ++i) { for (j=1; j<=m; ++j) _ans[i][j] = tmp[i][j]; } } memcpy(_m, _ct, sizeof(_m)); return ; } // 翻转第一行, 记得翻转的同时,周围的格子也会改变. dfs(now+1, step); tmp[1][now] = 1; _m[1][now] = !_m[1][now]; _m[1][now+1] = !_m[1][now+1]; _m[1][now-1] = !_m[1][now-1]; _m[2][now] = !_m[2][now]; dfs(now+1, step+1); tmp[1][now] = 0; _m[1][now] = !_m[1][now]; _m[1][now+1] = !_m[1][now+1]; _m[1][now-1] = !_m[1][now-1]; _m[2][now] = !_m[2][now]; } /* 4 4 1 0 0 1 0 1 1 0 0 0 0 0 0 0 0 0 */ int main() { int i, j; while (~scanf("%d%d", &n, &m)) { memset(_ans, 0x3f, sizeof(_ans)); memset(tmp, 0, sizeof(tmp)); int tmp = 0; int data; for (i=1; i<=n; ++i) for (j=1; j<=m; ++j) scanf("%d", &_m[i][j]); ishave = false; qstep = 0x3f3f3f3f; dfs(1, 0); if (ishave) { for (i=1; i<=n; ++i) { printf("%d", _ans[i][1]); for (j=2; j<=m; ++j) printf(" %d", _ans[i][j]); printf(" "); } } else printf("IMPOSSIBLE "); } return 0; }
Find The Multiple POJ - 1426
给你一个数字,要你求出他的倍数,这个倍数由0和1组成.
这个题利用了求模的一个性质 a%mod <==> (b*10+c)%mod 其中 a = b * 10 + c
所以,我们直接暴力搜过去就OK

#include <cstdio> using namespace std; char ans[105]; bool isover; bool dfs(int val, int n, int deep) { if (!val) { ans[deep] = '