IDA*学习笔记(uva10384和codevs2495)

迭代加深和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:8
int 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到5
int 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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值