只有学会了剪枝的搜索,才叫会搜索
可见我以前都不会搜索
DFS(深搜)
这里写一个深搜的板子,很抽象,要理解。因为实际题目可能更抽象。
type dfs(type x······)
{
if()//设置递归边界
{
//判断最优解
ans=____
return;
}
//到达搜索边界
if() return;
for()
{
if()
{
//修改全局变量
vis[]=1;
//搜索子节点
dfs(son of x);
//撤销修改,回溯
vis[]=0;
}
}
}
P1219 [USACO1.5]八皇后 Checker Challenge
八皇后问题,所有学dfs肯定会遇到的一个经典题目,主要的就是对于标记数组的使用
#include<iostream>
using namespace std;
#include<iomanip>
#include<string>
#include<algorithm>
#include<stack>
#include<map>
#include<queue>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<cstdio>
typedef unsigned long long ULL;
typedef long long LL;
typedef long L;
int n,total;
int a[100],b[100],c[100],d[100];
void present()
{
if(total<=2)
{
for(int k=1;k<=n;++k)
{
printf("%d ",a[k]);
}
printf("\n");
}
++total;
}
void queen(int i)
{
if(i>n)
{
present();
return ;
}
else
{
for(int j=1;j<=n;++j)
{
if( (!b[j])&&(!c[i+j])&&(!d[i-j+n]) )
{
a[i]=j;
b[j]=1;
c[i+j]=1;
d[i-j+n]=1;
queen(i+1);
b[j]=0;
c[i+j]=0;
d[i-j+n]=0;
}
}
}
}
int main()
{
scanf("%d",&n);
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(c,0,sizeof(c));
memset(d,0,sizeof(d));
total=0;
queen(1);
printf("%d",total);
return 0;
}
这道题递归结束边界,搜索终止条件十分明显,子节点搜索也很清楚,比较好
#include<iostream>
using namespace std;
#include<iomanip>
#include<string>
#include<algorithm>
#include<stack>
#include<map>
#include<queue>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<cstdio>
typedef unsigned long long ULL;
typedef long long LL;
typedef long L;
typedef double DB;
int n,a,b,ans;
int arr[300];
bool vis[300];
void dfs(int x,int step)
{
if(x==b)
{
ans=min(ans,step);
return ;
}
if(step > ans)
return ;//减枝
if( x<1 || x>n )
return ;
vis[x]=1;
if( !vis[x+arr[x]] )
dfs(x+arr[x],step+1);
if( !vis[x-arr[x]] )
dfs(x-arr[x],step+1);
vis[x]=0;
}
int main()
{
scanf("%d%d%d",&n,&a,&b);
for(int i=1;i<=n;++i)
scanf("%d",&arr[i]);
ans=1e9;
vis[a]=1;
dfs(a,0);
if(ans!=1e9)
cout << ans;
else
cout << -1;
return 0;
}
dfs更新方案数
#include<iostream>
using namespace std;
#include<iomanip>
#include<string>
#include<algorithm>
#include<stack>
#include<map>
#include<queue>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<cstdio>
typedef unsigned long long ULL;
typedef long long LL;
typedef long L;
typedef double DB;
int n,m,t;
int sx,sy,ex,ey,ans;
int Map[6][6];
int vis[6][6];
int dx[]={0, 0,0,1,-1};
int dy[]={0,-1,1,0, 0};
void dfs(int x,int y)
{
if(x==ex && y==ey)
{
ans++;
return ;
}
else
{
for(int i=1;i<=4;++i)
{
int tx=x+dx[i],ty=y+dy[i];
if( !vis[tx][ty] && Map[tx][ty] )
{
vis[tx][ty]=1;
dfs(tx,ty);
vis[tx][ty]=0;
}
}
}
}
int main()
{
scanf("%d%d%d",&n,&m,&t);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
Map[i][j]=1;
scanf("%d%d%d%d",&sx,&sy,&ex,&ey);
vis[sx][sy]=1;
for(int i=1;i<=t;++i)
{
int tx,ty;
scanf("%d%d",&tx,&ty);
Map[tx][ty]=0;
}
dfs(sx,sy);
cout << ans;
return 0;
}
主要是三维空间球体之间的几何情况,而且一开始准备打表建个查询图的,然后没想到每次算dist居然过了。结构体的重载 < < <号,其实也可以用cmp函数替代
#include<iostream>
using namespace std;
#include<iomanip>
#include<string>
#include<algorithm>
#include<stack>
#include<map>
#include<queue>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<cstdio>
typedef unsigned long long ULL;
typedef long long LL;
typedef long L;
typedef double DB;
const int maxn=1e3+10;
struct node
{
double x,y,z;
bool operator< (const node &B) const
{
return z<B.z;
}
}p[maxn];
int t;
int n,h,r;
bool vis[maxn];
bool flag;
double dist(node p1,node p2)
{
return sqrt( (p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y) + (p1.z-p2.z)*(p1.z-p2.z) );
}
void dfs(node x,int pos)
{
if( x.z+r >= h )
{
flag=true;
return ;
}
vis[pos]=1;
for(int i=pos;i<n;++i)
{
if(flag)
return ;
if( !vis[i] && dist(x,p[i])<=r*2 )
dfs(p[i],i);
}
vis[pos]=0;
}
int main()
{
scanf("%d",&t);
while(t--)
{
memset(vis,0,sizeof(vis));
memset(p,0,sizeof(p));
scanf("%d%d%d",&n,&h,&r);
for(int i=0;i<n;++i)
scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].z);
sort(p,p+n);
flag=false;
for(int i=0;i<n;++i)
{
if(p[i].z-r<=0)
dfs(p[i],i);
}
if(flag)
printf("Yes\n");
else
printf("No\n");
}
return 0;
}
BFS(广搜)
抽象板子
type bfs(type x)
{
queue<type> q;
q.push(x);
while(!q.empty())
{
type tx=q.front();
q.pop();
for()
{
//一系列操作
}
}
}
bfs的板子题,非常的典型
#include<iostream>
using namespace std;
#include<iomanip>
#include<string>
#include<algorithm>
#include<stack>
#include<map>
#include<queue>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<cstdio>
typedef unsigned long long ULL;
typedef long long LL;
typedef long L;
const int maxn=400+10;
int n,m,x,y;
int vis[maxn][maxn];
int dx[]={0,-2,-2, 2, 2,-1,-1, 1, 1};
int dy[]={0,-1, 1,-1, 1,-2, 2,-2, 2};
queue<int> q;
void bfs(int sx,int sy)
{
vis[sx][sy]=0;
q.push(sx);
q.push(sy);
while(!q.empty())
{
int tx=q.front();
q.pop();
int ty=q.front();
q.pop();
for(int i=1;i<=8;++i)
{
if( (tx+dx[i])>0 && (tx+dx[i])<n+1 && (ty+dy[i])>0 && (ty+dy[i])<m+1 && vis[tx+dx[i]][ty+dy[i]]==-1 )
{
vis[tx+dx[i]][ty+dy[i]]=vis[tx][ty]+1;
q.push(tx+dx[i]);
q.push(ty+dy[i]);
}
}
}
}
int main()
{
scanf("%d%d%d%d",&n,&m,&x,&y);
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
vis[i][j]=-1;
}
}
bfs(x,y);
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
printf("%-5d",vis[i][j]);
printf("\n");
}
return 0;
}
bfs直接标记,一套连招带走 可以用来增强自己bfs的自信
#include<iostream>
using namespace std;
#include<iomanip>
#include<string>
#include<algorithm>
#include<stack>
#include<map>
#include<queue>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<cstdio>
typedef unsigned long long ULL;
typedef long long LL;
typedef long L;
typedef double DB;
int n,m;
char Map[200][200];
int ans[200][200];
int vis[200][200];
int dx[]={0,1,-1,0, 0,-1,-1, 1,1};
int dy[]={0,0, 0,1,-1,-1, 1,-1,1};
void bfs(int x,int y,int cnt)
{
queue<int> q;
q.push(x);
q.push(y);
vis[x][y]=0;
while(!q.empty())
{
int tx=q.front();
q.pop();
int ty=q.front();
q.pop();
for(int i=1;i<=8;++i)
{
int rx,ry;
rx=tx+dx[i];
ry=ty+dy[i];
if(vis[rx][ry])
{
q.push(rx);
q.push(ry);
vis[rx][ry]=0;
}
}
}
}
int main()
{
while( scanf("%d%d",&n,&m)!=EOF )
{
if( n==0 && m==0 )
break;
for(int i=1;i<=n;++i)
scanf("%s",Map[i]+1);
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
if(Map[i][j]=='@')
vis[i][j]=1;
}
}
int cnt=0;
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
if(vis[i][j])
bfs(i,j,++cnt);
}
}
printf("%d\n",cnt);
}
return 0;
}
这道题bfs标记时间,主要是火不止一个,队列的储存的更新,烧到某个地块的时间的更新,和那个可怜的joe奔跑的时间比较。
#include<iostream>
using namespace std;
#include<iomanip>
#include<string>
#include<algorithm>
#include<stack>
#include<map>
#include<queue>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<cstdio>
typedef unsigned long long ULL;
typedef long long LL;
typedef long L;
typedef double DB;
const int maxn=1e3+10;
int t;
int r,c,ans;
char Map[maxn][maxn];
bool vis[maxn][maxn],vis2[maxn][maxn];
int fire[maxn][maxn];
int joe[maxn][maxn];
int dx[]={0,1,-1,0, 0};
int dy[]={0,0, 0,1,-1};
void joebfs(int x,int y)
{
queue<int> q;
q.push(x);
q.push(y);
joe[x][y]=1;
while(!q.empty())
{
int tx=q.front();
q.pop();
int ty=q.front();
q.pop();
if( tx==1 || tx==r || ty==1 || ty==c )
{
ans=min(ans,joe[tx][ty]);
break;
}
for(int i=1;i<=4;++i)
{
int rx=tx+dx[i],ry=ty+dy[i];
if( vis[rx][ry] && joe[tx][ty]+1<fire[rx][ry] && joe[tx][ty]+1<joe[rx][ry] )
{
joe[rx][ry]=joe[tx][ty]+1;
q.push(rx);
q.push(ry);
}
}
}
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&r,&c);
for(int i=1;i<=r;++i)
scanf("%s",Map[i]+1);
memset(vis,0,sizeof(vis));
// memset(vis,0,sizeof(vis2));
memset(fire,1,sizeof(fire));
memset(joe,1,sizeof(joe));
int sx,sy;
queue<int> q;
for(int i=1;i<=r;++i)
for(int j=1;j<=c;++j)
{
if(Map[i][j]!='#')
vis[i][j]=1;
if(Map[i][j]=='J')
sx=i,sy=j;
if(Map[i][j]=='F')
{
q.push(i);
q.push(j);
fire[i][j]=1;
}
}
while(!q.empty())
{
int tx,ty;
tx=q.front();
q.pop();
ty=q.front();
q.pop();
for(int i=1;i<=4;++i)
{
int rx=tx+dx[i],ry=ty+dy[i];
if( vis[rx][ry] && fire[tx][ty]+1<fire[rx][ry] )
{
fire[rx][ry]=fire[tx][ty]+1;
q.push(rx);
q.push(ry);
}
}
}
ans=1e9;
joebfs(sx,sy);
if(ans!=1e9)
printf("%d\n",ans);
else
printf("IMPOSSIBLE\n");
}
return 0;
}
剪枝
现在,你看到了一个搜索里最nb的技巧。剪枝的意义在于能够让暴力搜索减少了复杂时间复杂度,让我们能够过检测点拿到分数 但是同时剪枝的代码转化,来自于情况的各种优化,如果你不能分析出优化在哪里,那你就剪枝不成功,你就不能ac。
1、将所给的木棍从大到小排列,从大到小进行搜索,不然每次都要全搜一遍。
2、因为搜索的顺序,如果最后一次拼接失败,那么和最后一样长的木棍肯定也是失败的,所有不再尝试最后一次拼接失败的长度
3、如果在当前原始木棍中第一次尝试拼接木棍的递归分支就返回失败,剩下的都是等价情况,其他原始木棍情况也会失败
4、如果当前原始木棍恰好被拼好,而剩下的原始木棍拼接失败,那么当前分支就是失败的,已经不用继续搜下去了
这道题用了4个剪枝,就是4个优化,剪枝1,2比较容易理解,剪枝3、4有一定的难度,限定时间内不一定能想出来,而且剪枝3,4有着一定的联系性
听不明白的话,是我语文不行,我已经尽力了
这道题暴搜吸肼都过不了,主要是剪枝。
#include<iostream>
using namespace std;
#include<iomanip>
#include<string>
#include<algorithm>
#include<stack>
#include<map>
#include<queue>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<cstdio>
typedef unsigned long long ULL;
typedef long long LL;
typedef long L;
typedef double DB;
int n,cnt1;
int a[100],v[100],cnt,len;
bool cmp(int x,int y)
{
return x>y;
}
bool dfs(int stick,int cab,int last)
{
if( stick>cnt )
return true;
if( cab==len )
return dfs(stick+1,0,1);
int fail=0;
for(int i=last;i<=cnt1;++i)
{
if( !v[i] && cab+a[i]<=len && fail!=a[i] )
{
v[i]=1;
if( dfs(stick,cab+a[i],i+1) )
return true;
fail=a[i];
v[i]=0;
if( cab==0 || cab+a[i]==len )
return false;
}
}
return false;
}
int main()
{
int temp;
cnt1=1;
int sum=0;
scanf("%d",&n);
while(n--)
{
scanf("%d",&temp);
if(temp<=50)
{
a[cnt1++]=temp;
sum+=temp;
}
}
sort(a+1,a+cnt1,cmp);
cnt1--;
for(len=a[1];len<=sum;++len)
{
if( sum%len )
continue;
cnt=sum/len;
memset(v,0,sizeof(v));
if(dfs(1,0,1))
break;
}
printf("%d",len);
return 0;
}