https://blog.csdn.net/u011437229/article/details/53188837 图来自这个
个人分类:
1、固定深度的深搜
类似八皇后 N皇后 hdu2553 一个小剪枝是和前面已经安放好的行比较
首先明确什么时候返回啥
接着每层的枚举范围是啥,剪枝函数、约束函数等 进入下一层
思路:每层dfs放置一行的皇后,枚举皇后放的位置,判断是否和前面的皇后有冲突。当所有行都放完了,则找到一个可行解
注意这道题不打表会T
//A
#include<iostream>
using namespace std;
int n;
int cnt;
int col[15];
void dfs(int layer)
{
if(layer==n) ++cnt;
else
{
for(int i=0;i<n;++i)//枚举每列
{
col[layer]=i; //第layer个皇后被放在第i列
int ok=true;
for(int j=0;j<layer;++j)//枚举之前行的皇后
{
if(col[layer]==col[j]||layer-col[layer]==j-col[j]||layer+col[layer]==j+col[j])
{
ok=false;
break;
}
}
if(ok) dfs(layer+1);
}
}
}
int ans[15]={0,1,0,0,2,10,4,40,92,352,724};
int main()
{
// for(int i=1;i<=10;++i)
// {
// n=i;
// cnt=0;
// dfs(0);
// ans[i]=cnt;
// }
// for(int i=1;i<=10;++i)
// {
// cout<<ans[i]<<endl;
// }
while(cin>>n)
{
if(!n)
break;
cout<<ans[n]<<endl;
}
}
给定n个数,从1~n形成一个环,要求相邻的两个数之和为素数
自己写的时候会忘记回溯。。。
#include<iostream>
#include<string.h>
#include<stdio.h>
using namespace std;
const int N=30;
int prime[N];
int tag[N];
int cnt;
int vis[N],ans[N];//标记是否填充过以及每个排列的答案
int n;
void getp()
{
memset(tag,0,sizeof(tag));
tag[0]=tag[1]=1;
for(int i=2;i<N;++i)
{
if(!tag[i])
prime[++cnt]=i;
for(int j=1;j<=cnt&&i*prime[j]<N;++j)
{
tag[i*prime[j]]=1;
if(i%prime[j]==0) break;
}
}
}
//初始层数layer=1 ans[1]=1
void dfs(int layer)//从1-layer
{
if(layer==n&&!tag[ans[n]+1]) //tag=0 表示是素数
{
for(int i=1;i<n;++i)
printf("%d ",ans[i]);
printf("%d\n",ans[n]);
}
else
{
for(int i=2;i<=n;++i)
{
if(!vis[i]&&!tag[ans[layer]+i]) //以当前层去搜下一层,下一层要和本层相加为素数 下一个数没有被用过
{
ans[layer+1]=i;
vis[i]=1;
dfs(layer+1);
vis[i]=0; //回溯
}
}
}
}
int main()
{
getp();
int kase=0;
while(cin>>n)
{
++kase;
printf("Case %d:\n",kase);
memset(ans,0,sizeof(ans));
memset(vis,0,sizeof(vis));
ans[1]=1;
dfs(1);
printf("\n");
}
return 0;
}
2、不固定深度的搜索
poj 1426(感觉广搜更好) 深搜限定搜索范围为19次,直接return
题意:给一个数n,找出它的倍数m(m!=0),m的数位不超过200,每一位非0即1
框架
if(found) return ;
if(满足条件) {操作;found=true;return;}
继续搜索
DFS版 要思考好下一步迭代会变成什么样
#include<iostream>
#include<queue>
using namespace std;
typedef unsigned long long LL; //用BFS比较好,数量不会特别大 ,否则必须用unsigned long long
bool found=false;
void dfs(LL t,int n,int k) //一个很赞的方式是用变量控制是否找到解了,找到就return
{
if(found) return ;
if(t%n==0) {found=true; cout<<t<<endl; return ;}
if(k==19) return ;//没有这句话的话会一直深搜导致最后越界
dfs(t*10,n,k+1);
dfs(t*10+1,n,k+1);
}
int main()
{
int n;
while(cin>>n&&n)
{
found=false;
dfs(1,n,0);
}
return 0;
}
BFS版
#include<iostream>
#include<queue>
using namespace std;
typedef long long LL;
void bfs(int n)
{
LL x=1;
queue<LL>q;
q.push(x);
while(!q.empty())
{
LL y=q.front();
q.pop();
if(y%n==0) // !y%n这种写法是错的
{
cout<<y<<endl;
return;
}
q.push(y*10);
q.push(y*10+1);
}
}
UVA 129
题意:按照字典序输出只用前L个字母组成的第N个hard sequence, 序列每四个字母空一格,满16组换行。序列最大长度为80
hard sequence 不存在两个相邻的相等子串
//参考了别人的代码,改了一个地方,感觉可读性更高https://blog.csdn.net/consciousman/article/details/52675360
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#define LL long long
#define INF 0x3f3f3f3f
#define N 1010
using namespace std;
int n, l, tag, cnt;
char ans[100000];
int check(char *s, int len)
{
int x = len/2;
for (int i = 1; i <= x; i++){ //相同的子串最长是x,这个是检查加上a[len-1]后是否存在长度为i的相邻相同子串形成 ABA 加上A就是easy的
int flag = 1;
for (int j = len-1, tot = 0; tot < i && flag; j--, tot++)
if (s[j] != s[j-i]) flag = 0; //长度为i的子串对比中只要有一个不相等就OK
if (flag) return 0;
}
return 1;
}
void dfs(int len)
{
if(!tag) return ;
if (cnt++ == n){
int x = 0, k = 0;
for (int i = 0; i < len; i++){
if (++k <= 4) putchar(ans[i]);
if (k == 4){
k = 0;
if (++x == 16 && i != len-1) putchar('\n'), x = 0;
else{
if (i != len-1) putchar(' ');
}
}
}
printf("\n%d\n", len);
tag = 0;
return;
}
for (int i = 0; i < l ; i++){
ans[len] = i+'A';
if (check(ans, len+1)) dfs(len+1);
else ans[len] = 0;
}
}
int main()
{
while (scanf("%d %d", &n, &l), n || l) cnt = 0, tag = 1, dfs(0);
return 0;
}
3、埃及分数 ——迭代加深搜
我写了三个版本的代码,第一个找到第一个答案就会退出,是错误的,
第二个是bool dfs版本的,第三个是void dfs版本的,其实都一样,不过bool 版本的容易判断结果是否找到,以前觉得bool版本很难理解,但是事实上void 版本return的地方对应看, 找到答案的return true ,继续找不可行 return false
//下面是我自己改的代码,A了,比上一个版本快了6ms
#include<iostream>
#include<cstring>
#include<stdio.h>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=1000;
LL ans[N],now[N];
LL maxd;
int ko=0;
LL find_best(LL a,LL b)//寻找1/i<a/b i>b/a
{
return b/a+1;
}
LL gcd(LL a,LL b)
{
return b==0?a:gcd(b,a%b);
}
bool better(LL maxd)
{
// for(LL d=maxd;d>=0;--d)
// {
// // if(now[d]<ans[d]||ans[d]==-1) return true; 大于等于都不更新 我只这么写的话没考虑大于的情况,大于也可以直接终止了
// if(now[d]!=ans[d]) return now[d]<ans[d]||ans[d]==-1;//这样写代码很简洁 ,思路也很值得学习 感觉贪心的时候经常这么错,忘记考虑相等的情况
// }
return now[maxd]<ans[maxd]||ans[maxd]==-1;
}
void dfs(LL deep,LL frac,LL aa,LL bb) //该深度下跑完所有的量
{
bool ok;
if(deep==maxd)
{
if(bb%aa) return ; //aa!=1 该层查找失败 只到最后一层才开始确认是埃及分数吗??但是在递归的过程中每层的now[d]的修改都是经过检查的
now[deep]=bb/aa;
// for(int i=0;i<=deep;++i) cout<<ans[i]<<" ";
// cout<<endl;
if(better(deep)) memcpy(ans,now,sizeof(LL)*(deep+1));
return ;
}
ok=false;
LL i=max(find_best(aa,bb),frac); //上一个传递下来的分母,要和现在要组成的数进行比较
for(i;;++i)
{
if((maxd+1-deep)*bb<=i*aa) break; //ok=false; return ok;
now[deep]=i;
//不剪枝的话就进行迭代,迭代的数为aa/bb-1/i;
LL b2=bb*i;
LL a2=aa*i-bb;
LL g=gcd(a2,b2);
dfs(deep+1,i+1,a2,b2); //如果下一深度找到最佳答案 就return ok 深度不够就一直递归增加深度! nice! 懂啦!
}
}
void solve(LL a,LL b)
{
int check=0;
for(maxd=1;;++maxd)
{
memset(ans,-1,sizeof(ans));
dfs(0,find_best(a,b),a,b);
if(ans[maxd]!=-1)
{
check=1;
break;
}
}
if(check)
{
for(int i=0;i<maxd;++i)
printf("%lld ",ans[i]);
printf("%lld\n",ans[maxd]);
}
}
int main()
{
LL a, b;
while(cin>>a>>b)
{
solve(a,b);
}
return 0;
}
4、hdu 1010 奇偶剪枝
真的是每次做题都能wa在不同的地方,一直死,这次是dfs传递变量的时候应该用cnt+1 ,而不是cnt++,会影响向别的方向搜索
//dfs(nx,ny,cnt+1) 不能写成cnt++否则会wa
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
using namespace std;
int N,M,T;
char mp[10][10];
int vis[10][10];//这里不需要VIS数组,因为每次访问它的时候把它设置成不可访问的那种
int sx,sy,dx,dy;
int flag=0;
int dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
void dfs(int x,int y,int cnt)
{
if(x==dx&&y==dy&&cnt==T) //如果找到了并且能退出
{
flag=1;
return;
}
if(x<0||x>=N||y<0||y>=M) return; //我只要找一条能走的通的路径,所以这里就可以不走
if(flag) return;
int tmp=T-cnt-fabs(dx-x)-fabs(dy-y);
if(tmp<0||tmp&1) return; //奇偶剪枝 T-cnt是还能走的步数,abs(dx-x)+abs(dy-y)是理论最短步数,如果奇偶性不一样一定不能走到
for(int i=0;i<4;++i)
{
int nx=x+dir[i][0];
int ny=y+dir[i][1];
if(mp[nx][ny]!='X') //一开始写成=='.'才能进入,那我永远也到不了终点了
{
mp[nx][ny]='X';
dfs(nx,ny,cnt+1); //不能写成cnt++,否则wa!!!!!!!!!!!!!!!!!!!!!!
mp[nx][ny]='.';
if(flag) return; //如果已经找到了没有必要回退后再往剩下的三个方向搜了
}
}
}
int main()
{
while(cin>>N>>M>>T)
{
if(!N&&!M&&!T) break;
memset(vis,0,sizeof(vis));
flag=0;
int wall = 0;
// memset(mp,0,sizeof(mp));
for(int i=0;i<N;++i)
for(int j=0;j<M;++j)
{
cin>>mp[i][j];
if(mp[i][j]=='S')
{
sx=i;
sy=j;
}
if(mp[i][j]=='D')
{
dx=i;
dy=j;
}
else if(mp[i][j] == 'X') ++wall;
}
if(N*M - wall <= T) {cout<<"NO"<<endl; continue;}
mp[sx][sy]='X'; //忘记置这个为不可访问了
dfs(sx,sy,0);
if(flag) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
12/13待更新,总觉得一些需要用dfs迭代计数的题我还是有点迷,比如最大流,还有一个最小生成树计数
int dfs(int x,int a)
{
if(x==t||!a) return a;
int flow=0,f=0;
for(int &i=cur[x];i<G[x].size();++i)
{
Edge &e=edges[G[x][i]];
if(d[e.to]==d[x]+1&&(f=dfs(e.to,min(a,e.cap-e.flow)))>0) //dfs找到了一条增广路
{
e.flow+=f;
edges[G[x][i]^1].flow-=f;
flow+=f;
a-=f;
if(!a) break;
}
}
return flow;
}