pku 1077

2009年8月11日 星期二

题目链接:PKU 1077 Eight  

分类:bfs,双向bfs,A*

题目分析与算法原型
         这道题目是前一阵子做的,一直没有写解题报告,主要是想找个时间好好的总结一下这道题,因为从这道题中收获了很多东西,比方说A*的大体思想,如何设计启发式函数使得其满足A*算法的约束性,还有就是全排列哈希的方式(不存在冲突),以及堆优化时遇到已经标记过的但是当前值更小的情况下如何去更新而不是再次插入(以前的时候我一直都采用的是再次插入,这样子空间的复杂度高起来了)等等........总的来说收获很多,所有需要好好地记录一下
        关于此题的A*解法(当然bfs或双向bfs也可以解决),8数码问题经典的启发式函数有两种:比较简单的是difference ( Status a,  Status b ), 其返回值是a 和b状态各位置上数字(空格除外)不同的次数。另一种比较经典的是曼哈顿距离   manhattan ( Status a, Status b ),其返回的是各个数字(除却空格)从a的位置到b的位置的最短距离的和。学过A*的都应该知道,若想设计的A*算法能够保证找到最优解,其启发式函数(f(n)=g(n)+h(n) ,其中f(n) 是节点n的估价函数,g(n)实在状态空间中从初始节点到n节点的实际代价,h(n)是从n到目 标节点最佳路径的估计代价
)必须满足两点:1.h(n)<h'(n),h'(n)为从当前节点到目标点的实际的最优代价值。2.每次扩展的节点的f值至少不比父节点的f值小。
       我们先来看第一个启发函数,即difference,容易看出此时第一个条件显然满足,关于第二个条件,g( n)以为搜索的深度,所以子节点的g比父节点的g大1,然而,每次将空白位置与周围的某个数字交换后(不算空格),对于交换的这个数字,有两种结果,I.其和目标的位置相同。II.和目标的位置不同,即就是说h最多减少1,或者不变,所以g+h要么不变,要么比原先大1,此时两个条件都满足,因此该启发式函数能保证找到最优解。
        再看第二个启发式函数,即 manhattan ,也容易看出其定符合第一个条件,对于第二个条件,因为不算空格,所以每次交换我们只考虑和空格交换的那个数字,可以发现那个数字最多离目标位置前进一个(或者不变,或者后退一个),也就是说h之多减少1个,然而g已经加了1,所以g+h至少和原来的相等,或者更大,这样一来,第二个条件也满足了,由此我们可以发现,这两个启发式函数都可以保证A*找到最优解(关于为什么满足了A*的这两个条件就一定保证能找到最优解,可以去参考一下相应的人工智能的书籍,里面有详细的证明)
        还有关于这道题的判重可采用全排列之哈希方式(点此链接)..........
         (PS:说起这道题,不得不提一件事,那就是我在写的时候将“des=now[i]-'0'-1;”不小心写成了“des=now[i]-1”,拜托,两个值整整差了48啊,而且我是将他作为D数组的一个下标,我的D数组才定义了D[10][10],这样你会发现显然已经数组越界了,然后神奇的事情居然就发生了,这样子居然........AC了!!!!!!!!!,orz............真不知道是不是偶的RP太好了,这样子能A掉,想想很不可思议,也正因为如此,使得我的程序当时出现了一个异常诡异的地方,这个诡异的地方说起来有点麻烦就不多说了,反正我一直都没找出是怎么回事,后来才发现这个致命的笔误,再次膜拜自己,居然这样子都能过,狂晕啊,RP太好了点吧.........,而且改这个错误之前的程序跑的是150ms左右,改了后就能跑到0ms了,很想不通,这个错误居然还能影响时间........orz.......orz........orz..........
           总结:RP的力量果然很强大,所以偶们都需要好好积攒RP,适当的时候说不定能创造奇迹.........)

Code:

  1
#include<stdio.h>
  2#include<string.h>
  3#include<math.h>
  4#include<time.h>
  5#include<stdlib.h>
  6#define max 365000
  7
  8char beg[12],end[12],pace[9][5]={"dr","dlr","dl","udr","udlr","udl","ur","ulr","ul"},cz[max],ss[50];
  9int count,pp,mincost[max],fm[max],place[max],jc[8]={1,2,6,24,120,720,5040,40320},D[10][10];
 10int a[9][2]={{0,0},{1,0},{2,0},{0,-1},{1,-1},{2,-1},{0,-2},{1,-2},{2,-2}},goal;
 11bool flag[max];
 12
 13struct node
 14{
 15    int pos,g,h,num;
 16    char s[10];
 17}
queue[max];
 18void down_min_heap(int n,int h)//n表示堆元素的个数,从0开始编号,从h开始往下调整
 19{
 20    int i=h,j=2*i+1;
 21    node temp=queue[i];
 22    while(j<n)
 23    {
 24        if(j<n-1&&queue[j].g+queue[j].h>queue[j+1].g+queue[j+1].h)j++;//若右孩子存在,且右孩子比较小,取右
 25        if(temp.g+temp.h<queue[j].g+queue[j].h)break;
 26        else
 27        {
 28            place[queue[j].num]=i;
 29            queue[i]=queue[j];
 30            i=j;
 31            j=2*i+1;
 32        }

 33    }

 34    queue[i]=temp;
 35    place[temp.num]=i;
 36}

 37void up_min_heap(int s)
 38{
 39    while (s>0&&queue[s].g+queue[s].h<queue[(s-1)/2].g+queue[(s-1)/2].h)     //从s开始往上调整
 40    
 41        place[queue[s].num]=(s-1)/2;
 42        place[queue[(s-1)/2].num]=s;
 43        node tt=queue[s];
 44        queue[s]=queue[(s-1)/2];
 45        queue[(s-1)/2]=tt;
 46        s=(s-1)/2
 47    }

 48}

 49node pop()
 50{
 51    node res=queue[0];
 52    queue[0]=queue[count-1];
 53    place[queue[count-1].num]=0;
 54    count--;
 55    down_min_heap(count,0);
 56    return res;
 57}

 58void push(node x)
 59{
 60    queue[count]=x;
 61    place[x.num]=count;
 62    count++;
 63    up_min_heap(count-1);
 64}

 65int cal(char s[])  //计算全排列的哈希值(唯一对应)
 66{
 67    int i,j,cnt,res=0;
 68    for(i=1;i<9;i++)
 69    {
 70        cnt=0;
 71        for(j=0;j<i;j++)if(s[j]>s[i])cnt++;
 72        cnt*=jc[i-1];
 73        res+=cnt;
 74    }

 75    return res;
 76}

 77int dist(int now ,int des)//计算两个位置之间的最小步数
 78{
 79    int px=(int)(fabs(a[now][0]-a[des][0])),py=(int)(fabs(a[now][1]-a[des][1]));
 80    return px+py;
 81}

 82
 83//启发函数采用曼哈顿距离,即从当前状态下的每个数字(空格除开),分别到目标状态下相应数字的最小步数之和
 84
 85int estimate(char now[])
 86{
 87    int res=0,i,des;
 88    for(i=0;i<9;i++)
 89    {
 90        if(now[i]!='0')
 91        {
 92            des=now[i]-'0'-1;
 93            res+=D[i][des];
 94        }

 95    }

 96    return res;
 97}

 98int change(char s[],char op,int pos) //互换位置,返回换后的空格位置
 99{
100    int end;
101    switch(op)
102    {
103    case 'u':end=pos-3;break;
104    case 'd':end=pos+3;break;
105    case 'l':end=pos-1;break;
106    case 'r':end=pos+1;break;
107    }

108    char mid=s[pos];
109    s[pos]=s[end];
110    s[end]=mid;
111    return end;///返回调整后空格的位置
112}

113void bfs(char beg[],char end[])
114{
115    int i,num;
116    queue[0].pos=pp;
117    queue[0].g=0;
118    queue[0].h=estimate(beg);
119    num=cal(beg);
120    queue[0].num=num;
121    strcpy(queue[0].s,beg);
122    count=1;
123
124    flag[num]=true;
125    cz[num]='*';
126    fm[num]=-1;
127    place[num]=0;
128    mincost[num]=queue[0].h;
129    while(count>0)
130    {
131        node tt=pop();
132        place[tt.num]=count;
133        if(tt.num==goal)     
134        {
135            char ans[1000];
136            int k=0,fp;
137            fp=tt.num;
138            while(fp!=num)
139            {
140                ans[k++]=cz[fp];
141                fp=fm[fp];
142            }

143            int jj;
144            for(jj=k-1;jj>=0;jj--)printf("%c",ans[jj]);
145            printf("\n");
146            return;
147        }

148        int len=strlen(pace[tt.pos]);
149        for(i=0;i<len;i++)
150        {
151            node x=tt;
152            x.pos=change(x.s,pace[tt.pos][i],x.pos);
153            int k=cal(x.s);
154            x.num=k;
155            x.g++;
156            x.h=estimate(x.s);        
157            if(!flag[k])
158            {
159                flag[k]=true;
160                mincost[k]=x.g+x.h;
161                fm[k]=tt.num;
162                cz[k]=pace[tt.pos][i];
163                push(x);
164            }

165            else if(flag[k]&&x.g+x.h<mincost[k])
166            {
167                mincost[k]=x.g+x.h;
168                fm[k]=tt.num;
169                cz[k]=pace[tt.pos][i];
170                queue[place[k]]=x;
171                up_min_heap(place[k]);
172            }

173        }

174    }

175}

176bool check(char beg[])//检测目标状态是否可达
177{
178    int i,j,cnt,res=0;
179    for(i=1;i<9;i++)
180    {
181        cnt=0;
182        for(j=0;j<i;j++)if(beg[j]>beg[i])cnt++;
183        res+=cnt;
184    }

185    for(i=0;i<9;i++)
186    {
187        if(beg[i]=='0')
188        {
189            res+=D[i][8];
190            break;
191        }

192    }

193    if((res%2)==0)return true;
194    else return false;
195}

196int  main()
197{
198    int i,j;
199    strcpy(end,"123456780");
200    goal=cal(end);
201
202    for(i=0;i<9;i++)
203        for(j=0;j<9;j++)D[i][j]=dist(i,j);
204    while(gets(ss))
205    {
206        int len=strlen(ss),pos=0;
207        memset(flag,false,sizeof(flag));
208        for(i=0;i<len;i++)
209        {
210            if(ss[i]=='x')//空格部分用0代替
211            {
212                pp=pos;
213                beg[pos++]='0';
214            }

215            else if(ss[i]>='1'&&ss[i]<='8')beg[pos++]=ss[i];
216        }

217        beg[9]='\0';
218        if(!check(beg))printf("unsolvable\n");
219        else bfs(beg,end);
220    }

221    return 1;
222}

223

posted on 2009-08-12 00:00 蜗牛也Coding 阅读(2408) 评论(4)  编辑 收藏 引用

评论

# re: pku 1077 2009-08-12 11:59 12530彩铃

不错啊~  回复  更多评论   

# re: pku 1077 2009-08-13 02:02 abilitytao

你好像对搜索情有独钟 呵呵   回复  更多评论   

# re: pku 1077 2009-08-13 09:39 蜗牛也Coding

@abilitytao
呵呵,只是最近一阵子在做搜索方面的题目而已.......  回复  更多评论   

# re: pku 1077 2010-07-05 14:48 tt

今天在研究你的代码 但是还有一些地方不太明白 不知道能否和你交流一下 我的QQ是64076241  回复  更多评论   


只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


<2009年8月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
303112345

导航

统计

常用链接

留言簿(8)

随笔档案(78)

搜索

积分与排名

最新评论

阅读排行榜

评论排行榜