IDA*学习笔记(uva10384和codevs2495)

来源:互联网 发布:金牛软件官网 编辑:程序博客网 时间:2024/05/29 15:42

迭代加深和A*都是常用的搜索方法,当它们融汇贯通的时候,会发生很奇妙的事情……

题意

有些格子上有墙,可以推墙到相邻格子上(前提是相邻格子的位置上没有墙),求走出迷宫的一条可行道路。

分析

由于要不断地推墙,所以可以走重复格子,这样就会无休止地走下去,所以我们只能用迭代加深,限定每次深搜走几步了。
然后是寻路的话自然可以用A*搞一搞,每次判断当前格子到最近出口的直线距离,如果当前步数+预计最短距离>限定步数的话,可以剪个枝。
最后随便搞一搞就AC了。

代码

#include<iostream>#include<cstdio>#include<cstring>#include<climits>#include<iomanip>#include<algorithm>#include<queue>using namespace std;int read(){    int q=0;char ch=' ';    while(ch<'0'||ch>'9')ch=getchar();    while(ch>='0'&&ch<='9')q=q*10+ch-'0',ch=getchar();    return q;}int sx,sy,d,tot;//W:1,N:2,E:4,S:8int ma[5][7];int mvx[5]={0,0,0,1,-1},mvy[5]={0,1,-1,0,0},wal[5]={0,4,1,8,2};int op[5]={0,1,4,2,8};char s[5]={' ','E','W','S','N'};char ans[1005];int h(int x,int y){    int i,re=INT_MAX;    for(i=1;i<=4;i++){        if(!(ma[i][1]&wal[2]))re=min(re,abs(x-i)+abs(y-1));        if(!(ma[i][6]&wal[1]))re=min(re,abs(x-i)+abs(y-6));    }    for(i=1;i<=6;i++){        if(!(ma[1][i]&wal[4]))re=min(re,abs(x-1)+abs(y-i));        if(!(ma[4][i]&wal[3]))re=min(re,abs(x-4)+abs(y-i));    }    return re;}bool dfs(int x,int y,int dep){    if(h(x,y)+dep>d)return 0;    if(dep==d){        if((x==1||x==4||y==1||y==6)&&!h(x,y)){        if(x==1&&!(ma[x][y]&wal[4]))ans[++tot]=s[4];        else if(x==4&&!(ma[x][y]&wal[3]))ans[++tot]=s[3];        else if(y==1&&!(ma[x][y]&wal[2]))ans[++tot]=s[2];        else ans[++tot]=s[1];        return 1;        }        return 0;    }    for(int i=1;i<=4;i++){        int tx=x+mvx[i],ty=y+mvy[i];        if(tx<1||ty<1||tx>4||ty>6)continue;        if(!(ma[x][y]&wal[i]))//没墙            {if(dfs(tx,ty,dep+1)){ans[++tot]=s[i];return 1;}}        else {//推墙            int ttx=tx+mvx[i],tty=ty+mvy[i];            if(ma[tx][ty]&wal[i])continue;            ma[x][y]-=wal[i];ma[tx][ty]+=wal[i];ma[tx][ty]-=op[i];            if(ttx>=1&&ttx<=4&&tty>=1&&tty<=6)ma[ttx][tty]+=op[i];            if(dfs(tx,ty,dep+1)){ans[++tot]=s[i];return 1;}            ma[x][y]+=wal[i];ma[tx][ty]-=wal[i];ma[tx][ty]+=op[i];            if(ttx>=1&&ttx<=4&&tty>=1&&tty<=6)ma[ttx][tty]-=op[i];        }    }    return 0;}int main(){    int i,j;    while(1){        sy=read();sx=read();        if(!sx&&!sy)break;        for(i=1;i<=4;i++)            for(j=1;j<=6;j++)ma[i][j]=read();        for(d=0;;d++){tot=0;if(dfs(sx,sy,0))break;}//从0开始        for(i=tot;i>=1;i--)printf("%c",ans[i]);        printf("\n");    }    return 0;}

拓展练习题

codevs2495水叮当的舞步。
中文题就不题意了…..
广搜没办法判重,肯定会爆空间。深搜又难剪枝,绝对会爆时间,这道题我用广搜做了两个多小时,只拿了10分,血的教训啊……
这种时候就要考虑迭代加深和A*的优化了,迭代加深可以缓解广搜空间的问题,而A*则可以成为深搜的极好优化手段。我们先想想迭代加深,那么可以规定最多跳多少步。而在当前状态下最优解是剩余颜色的数量,所以我们考虑用剩余颜色作为A*的估价函数。
还有一个优化图,就是将左上角联通色块变成“1”,它旁边的都变成“2”,剩下的变成“0”,这样方便我们写估价函数和寻找可以扩展的色块。
代码:

#include<iostream>#include<cstdio>#include<cstring>#include<climits>#include<cstdlib>#include<iomanip>#include<algorithm>using namespace std;//颜色是0到5int mvx[5]={0,1,-1,0,0},mvy[5]={0,0,0,1,-1};int ma[9][9],ok[9][9];//ma是毯子,ok是一个优化图int dlx[65],dly[65];bool v[6];int m,n,ans;int gj(int color){//估价函数,处理剩余颜色数量    memset(v,0,sizeof(v));    int i,j,sum=0;    for(i=1;i<=n;i++)      for(j=1;j<=n;j++){          if(ok[i][j]!=1&&v[ma[i][j]]==0){              v[ma[i][j]]=1;sum++;          }      }    return sum;}void bfs(int sx,int sy,int color){//采用广搜寻找扩展的状态    int i,j,k,tx,ty;    int head=1,tail=1;    dlx[1]=sx;dly[1]=sy;ok[sx][sy]=1;    while(head<=tail){        for(i=1;i<=4;i++){            tx=mvx[i]+dlx[head];            ty=mvy[i]+dly[head];            if(tx>=1&&tx<=n&&ty>=1&&ty<=n&&ok[tx][ty]==0){                if(ma[tx][ty]==color){                    ok[tx][ty]=1;                    tail++;dlx[tail]=tx;dly[tail]=ty;                    }                else {ok[tx][ty]=2;}//在联通块旁边            }        }        head++;    }}bool dfs(int d,int f,int now,int color){    int i,j,k;    int bj=0;    if(now+f>d){return 0;}//如果当前解+剩余最优解>规定层数,剪枝     if(f==0){return 1;}//如果没有其它色块了,就找到了解。     int bf[9][9];//注意,由于搜到下层会改变bf的值,所以bf要在函数里定义    for(i=0;i<=5;i++){        bj=0;        if(i==color)continue;//如果i=color相当于不染色        for(j=1;j<=n;j++)          for(k=1;k<=n;k++)bf[j][k]=ok[j][k];//bf数组备份一个ok        for(j=1;j<=n;j++)          for(k=1;k<=n;k++){              if(ok[j][k]==2&&ma[j][k]==i){                  bj=1;bfs(j,k,i);              }          }       if(bj==1){            if(dfs(d,gj(i),now+1,i)==1){return 1;}           for(j=1;j<=n;j++)             for(k=1;k<=n;k++)ok[j][k]=bf[j][k];       }    }    for(j=1;j<=n;j++)//将ok变回来        for(k=1;k<=n;k++)ok[j][k]=bf[j][k];    return 0;}int main(){   int i,j,k;   while(1){       scanf("%d",&n);       memset(ok,0,sizeof(ok));       if(n==0)break;       for(i=1;i<=n;i++)         for(j=1;j<=n;j++){             scanf("%d",&ma[i][j]);             }       bfs(1,1,ma[1][1]);//确认左上角联通块      for(ans=0;ans<=n*n;ans++){//一定要从0开始,不然一开始就是所有色块一样的毯子没有答案          if(dfs(ans,gj(ma[1][1]),0,ma[1][1])==1){                  printf("%d\n",ans);                  break;             }          }      }   return 0;}
0 0
原创粉丝点击