专题一搜索.docx
- 文档编号:30307056
- 上传时间:2023-08-13
- 格式:DOCX
- 页数:28
- 大小:21.40KB
专题一搜索.docx
《专题一搜索.docx》由会员分享,可在线阅读,更多相关《专题一搜索.docx(28页珍藏版)》请在冰豆网上搜索。
专题一搜索
1.HDOJ1044
题意:
在走出图中迷宫的情况下,收集更多的珠宝
方法:
非*点标号,各点之间跑DFS求最短路径值,BFS求各种可行情况的最优值
剪枝:
A若是@到<的值大于可行值,则无解
B当前DFS找到的解已经是所有珠宝的和了,不需要继续了(已经得到最优解)
SampleInput
3
4422
100200
****
*@A*
*B<*
****
4412
100200
****
*@A*
*B<*
****
125132
100200
************
*B.........*
*.********.*
*@...A....<*
************
SampleOutput
Case1:
Thebestscoreis200.
Case2:
Impossible
Case3:
Thebestscoreis300.
代码1:
intn,m,k;
intscore[150];
intdist[200][200];
charMap[550][550],in[550];
intdir_x[]={0,1,-1,0,0};
intdir_y[]={0,0,0,1,-1};
intvis[55],sum,ans,t;
voidbfs(inttx,intty,intnum)
{
inthead=1,tail=1,dir,nx,ny;
intsteps[55][55],visit[55][55];
struct
{
intx,y;
}que[2555];
Fill(steps,0);Fill(visit,0);Fill(que,0);
que[head].x=tx;
que[head].y=ty;
visit[tx][ty]=1;
while(head<=tail)
{
For2(dir,1,4)
{
nx=que[head].x+dir_x[dir],ny=que[head].y+dir_y[dir];
if(nx>=0&&nx visit[nx][ny]&&Map[nx][ny]! ='*') { steps[nx][ny]=steps[que[head].x][que[head].y]+1; tail++; que[tail].x=nx; que[tail].y=ny; visit[nx][ny]=1; if(Map[nx][ny]>='A'&&Map[nx][ny]<='J') dist[num][Map[nx][ny]-'A'+1]=steps[nx][ny]; elseif(Map[nx][ny]=='@') dist[num][0]=steps[nx][ny]; elseif(Map[nx][ny]=='<') dist[num][k+1]=steps[nx][ny]; } } head++; } } voiddfs(ints,intvalue,inttime) { inti; if(time>t)return;//超出限制时间 if(ans==sum)return;//已经得到了最大的价值,剪枝(没有这个会超时) //借鉴的kuangbin大神的高级思路! if(s==k+1) { if(value>ans)ans=value; return; } For2(i,1,k+1) { if(dist[s][i]==0||vis[i])continue; vis[i]=true; dfs(i,value+score[i],time+dist[s][i]); vis[i]=false; } } intmain() { //input; inti,j,Case; cin>>Case; for(intCase_num=1;Case_num<=Case;Case_num++) { getchar(); Fill(score,0);Fill(dist,0);Fill(score,0);Fill(Map,0);Fill(vis,0);sum=0,ans=-1; //一切有用的东西赋初值! cin>>m>>n>>t>>k; For2(i,1,k)Sca_d(score[i]),sum+=score[i];//输入分值的和时,顺便计算所有分值的和。 方便剪枝 For2(i,0,n-1) { Sca_s(in); For2(j,0,m-1) Map[i][j]=in[j]; } For2(i,0,n-1) For2(j,0,m-1) if((Map[i][j]>='A'&&Map[i][j]<='J')) bfs(i,j,Map[i][j]-'A'+1);//A表示为1,依次类推 elseif(Map[i][j]=='@') bfs(i,j,0);//起点标号为0 /*For2(i,0,k) { For2(j,0,k+1) printf("%d",dist[i][j]); cout< } cout< //写DFS为了检验BFS的正确性作的中间过程输出 printf("Case%d: \n",Case_num); dfs(0,0,0); if(ans>=0)printf("Thebestscoreis%d.\n",ans); elseprintf("Impossible\n"); /*if(dist[0][k+1]>t)printf("Impossible\n"); //不理解用dist[0][k+1]作为判断条件为啥WA else { dfs(0,0,0); printf("Thebestscoreis%d.\n",ans); }*/ if(Case_num } return0; } 代码2: 状态压缩搜索 intn,m,l,cnt,ans; intsx,sy,ex,ey; inta[15]; intdx[]={-1,1,0,0}; intdy[]={0,0,-1,1}; boolvis[maxn][maxn][1<<10]; chars[maxn]; charmp[maxn][maxn]; structNode { intx,y,step,val; intstate; }cur,now; queue boolbfs() { inti,j,nx,ny,tx,ty,nstep,nval,nst,tst,tmp; inthead=0,tail=-1; memset(vis,0,sizeof(vis)); while(! q.empty())q.pop(); ans=-1; cur.x=sx; cur.y=sy; cur.step=0; cur.val=0; cur.state=0;//state表示当前有哪些字母表示的分值已经使用过 q.push(cur); vis[sx][sy][0]=1; while(! q.empty()) { now=q.front(); q.pop(); nx=now.x; ny=now.y; nstep=now.step; nval=now.val; nst=now.state; if(nstep>l)break;//注意退出条件 if(nx==ex&&ny==ey)//更新ans { if(ans } for(i=0;i<4;i++) { tst=nst; tx=nx+dx[i]; ty=ny+dy[i]; cur.val=nval; if(mp[tx][ty]>='A'&&mp[tx][ty]<='J') { tmp=mp[tx][ty]-'A';//A字母标号为0,B字母标号为1,依次类推 if(! (tst&(1< //如果当前状态tst变量中第tmp位为0,那么可以取 tst=tst|(1< 若tst第tmp位为1,则不变;若tst第tmp位为0,则更新 } if(tx>=1&&tx<=n&&ty>=1&&ty<=m&&mp[tx][ty]! ='*'&&! vis[tx][ty][tst]) { vis[tx][ty][tst]=1; cur.x=tx; cur.y=ty; cur.step=nstep+1; cur.state=tst; q.push(cur); } } } if(ans==-1)returnfalse; returntrue; } intmain() { inti,j,t,t1=0; scanf("%d",&t); while(t--) { t1++; scanf("%d%d%d%d",&m,&n,&l,&cnt); for(i=0;i { scanf("%d",&a[i]); } for(i=1;i<=n;i++) { scanf("%s",s); for(j=1;j<=m;j++) { mp[i][j]=s[j-1]; if(s[j-1]=='@') { sx=i; sy=j; } if(s[j-1]=='<') { ex=i; ey=j; } } } if(t1>1)printf("\n"); printf("Case%d: \n",t1); if(bfs())printf("Thebestscoreis%d.\n",ans); elseprintf("Impossible\n"); } return0; } 2.Fliptile 题目大意就是说一个棋盘的黑白棋,反转一个周围的也会反转,然后问都变成0的最小次数的时候,每一个分别反转多少次。 首先应该想到说每一个最多反转1次,因为反转2次和0次是等价的,所以说每一个就是0或1了。 然后就是说第一行确定了的话第二行也会确定下来,因为第一行某一个的上左右都确定下来了,所以下也会确定,所以可以通过第一行推出之后的所有。 然后就是第一行的问题了。 N和M都小于等于15,2^N不会超时。 直接枚举第一行就行。 SampleInput 44 1001 0110 0110 1001 SampleOutput 0000 1001 1001 0000 intn,m; intmapp[mxn][mxn]; intans[mxn][mxn]; intmem[mxn][mxn]; intmx[]={0,1,0,-1},my[]={1,0,-1,0}; boolok(intx,inty){ if(x<0||y<0||x>=m||y>=n)returnfalse; returntrue; } boolcalc(intx,inty){ intret=mapp[x][y]+mem[x][y]; for(inti=0;i<4;++i){ inttx=x+mx[i]; intty=y+my[i]; if(ok(tx,ty)) ret+=mem[tx][ty]; } returnret%2; } voidnxt(intx,inty,int&tx,int&ty){//搜索下一个坐标 tx=x; ty=y+1; if(ty==n){ ++tx; ty=0; } } intdfs(intx,inty,intnum){ if(x==m)returnnum; booltem=calc(x-1,y); if(tem){ mem[x][y]=true; ++num; } inttx,ty; nxt(x,y,tx,ty); returndfs(tx,ty,num); } intsearch(intin){ memset(mem,false,sizeof(mem)); intret=0; for(inti=0;i mem[0][i]=true; ++ret; } ret+=dfs(1,0,0); for(inti=0;i returninf; returnret; } intmain(){ while(scanf("%d%d",&m,&n)! =EOF){ for(inti=0;i for(intj=0;j scanf("%d",&mapp[i][j]); inttem=inf; for(inti=0;i<(1< intrec=search(i); if(tem>rec){ tem=rec; memcpy(ans,mem,sizeof(mem)); } } if(tem==inf){ puts("IMPOSSIBLE"); continue; } for(inti=0;i printf("%d",ans[i][0]); for(intj=1;j printf("%d",ans[i][j]); puts(""); } } return0; } 3.状态压缩HDOJ1429胜利大逃亡(续) 分析: 题目是求最少的逃亡时间,确定用BFS 这个题目的难点在于有几个锁对于几把钥匙,唯一的对应关系,不能用直接的标记法,因为一个图可能需要搜索多次。 仔细分析的话会发现,图的搜索次数是和钥匙的出现次数相关,那么我们可以用二进制的0和1来表示第几把钥匙出现过没有,所以我们可以用状态压缩来标记那个钥匙出现过,然后用三维标记,第三维表示出现几个钥匙了的情况下图的点的搜索情况。 其他就和简单的一样。 SampleInput 4517 @A.B. a*.*. *..*^ c..b* 4516 @A.B. a*.*. *..*^ c..b* SampleOutput 16 -1 constintmaxn=30; constintmaxm=30; intn,m,T; intsx,sy; charg[maxn][maxm]; structnode { intx,y,key,dist; }que[maxn*maxm*1100]; intvis[maxn][maxm][1100]; intdx[]={0,0,0,1,-1}; intdy[]={0,1,-1,0,0}; voiddebug() { inti,j; For2(i,1,n) For2(j,1,m) printf("%c%c",g[i][j],j==m? '\n': ''); } intmain() { //input; inti,j,k; while(scanf("%d%d%d",&n,&m,&T)! =EOF) { Fill(g,0); getchar();//记得这个把nmT一行的回车读走 For2(i,1,n) gets(g[i]+1); For2(i,1,n) For2(j,1,m) if(g[i][j]=='@') sx=i,sy=j; //debug(); Fill(vis,0); Fill(que,0); inthead=0,tail=0; intflag=0;//表示目标未到 que[0].x=sx;que[0].y=sy;que[0].key=0;que[0].dist=0;//初始化起点 vis[sx][sy][0]=1;//防止在起点处重复搜索 while(head<=tail&&! flag) { node&h=que[head]; //if(h.dist>=T)break; For2(k,1,4) { intnx=h.x+dx[k]; intny=h.y+dy[k]; if(nx>=1&&nx<=n&&ny>=1&&ny<=m&&g[nx][ny]! ='*'&&vis[nx][ny][h.key]==0) { if(g[nx][ny]=='.'||g[nx][ny]=='@') { tail++; node&t=que[tail]; t.x=nx; t.y=ny; t.key=h.key; t.dist=h.dist+1; vis[nx][ny][h.key]=1; } elseif(g[nx][ny]=='^') { tail++; node&t=que[tail]; t.x=nx; t.y=ny; t.key=h.key; t.dist=h.dist+1; flag=t.dist; vis[nx][ny][h.key]=1; } elseif(g[nx][ny]>='a'&&g[nx][ny]<='z')//拾取钥匙 { vis[nx][ny][h.key]=1; intcount=g[nx][ny]-'a'; inttempkey=1< tail++; node&t=que[tail]; t.x=nx;t.y=ny; t.key=h.key|tempkey;//拾取钥匙 t.dist=h.dist+1; } elseif(g[nx][ny]>='A'&&g[nx][ny]<='Z') { intcount=g[nx][ny]-'A'; inttempkey=1< if(h.key&tempkey)//判断我手中的钥匙能不能打开这扇门 { tail++; node&t=que[tail]; t.x=nx; t.y=ny; t.key=h.key; t.dist=h.dist+1; vis[nx][ny][h.key]=1; } } } } head++; } if(flag elseprintf("-1\n"); } return0; } 4.非常可乐(BFS)(有更好的数学方法做gcd(a,b)) 难点: 新节点产生的表示方法 用容量为A,B的量杯能否倒出容量为C的水 SampleInput 743 413 000 SampleOutput NO 3 structnode { intv[5]; }; intV[5]; intd[101][101][101]; queue voidpour(nodetp,intp,intq) { nodex; x.v[6-p-q]=tp.v[6-p-q];//这个杯子的水在这种情况的转换是不动的 if(V[q]-tp.v[q]>=tp.v[p]) { x.v[q]=tp.v[q]+tp.v[p]; x.v[p]=0; } else { x.v[q]=V[q]; x.v[p]=tp.v[p]-(V[q]-tp.v[q]); } if(d[x.v[1]][x.v[2]][x.v[3]]==-1) { d[x.v[1]][x.v[2]][x.v[3]]=d[tp.v[1]][tp.v[2]][tp.v[3]]+1; que.push(x); } else { d[x.v[1]][x.v[2]][x.v[3]]=min(d[x.v[1]][x.v[2]][x.v[3]],d[tp.v[1]][tp.v[2]][tp.v[3]]+1); } } intmain() { intS,N,M; while(cin>>S>>N>>M,S+N+M) { if(S%2) { printf("NO\n"); continue; } V[1]=S;V[2]=N;V[3]=M; while(! que.empty())que.pop(); memset(d,-1,sizeof(d)); nodetp; tp.v[1]=S,tp.v[2]=0,tp.v[3]=0; d[S][0][0]=0; que.push(tp); nodex; while(! que.empty()) { tp=que.front(); que.pop(); pour(tp,1,2); pour(tp,1,3); pour(tp,2,1); pour(tp,2,3); pour(tp,3,1); pour(tp,3,2); } intans=999999; stack while(! a.empty())a.pop(); intk=S/2; if(S>=k&&N>=k)a.push(d[k][k][0]); if(S>=k&&M>=k)a.push(d[k][0][k]); if(N>=k&&M>=k)a.push(d[0][k][k]); while(! a.empty()) { if(a.top()! =-1)ans=min(ans,a.top()); a.po
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 专题 搜索