现在的位置: 首页 > 综合 > 正文

HDU(3567):八数码问题(升级版)——双BFS

2019年09月03日 ⁄ 综合 ⁄ 共 3759字 ⁄ 字号 评论关闭

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3567

 

有了HDU 1043的基础,此题就变得比较容易了,如果用A*算法,感觉路径不太好打印,所以就放弃了,看到有一种好像和1043一样的打表法,但是没怎么能理解,也就放弃了,虽然要打印最小字典序,单向BFS显然会TLE,所以我只能用双向BFS了。

 

好在,此题的输入保证有解,否则还得用一下八数码的逆序数定理进行判断是否有解,即:逆序数相同则有解,否则无解。

 

整体代码和1043的差不多,只是把路径的打印处理一下。虽然看似简单,但还是,WA了好多次。

第一次WA,发现了操作为0是的输出有错误。

第二次WA,发现了要想打印最小字典序,只能由正向BFS输出,即使反向遍历是出现了路径

第三次WA,发现了在方向遍历时,某个点如果已经被反向遍历,不能不对其操作,还得进行一次比较操作,看其是否是最小的字典序

主要是这三次,后来就AC了,时间1.3s,和大神的差距不小。

 

双向BFS代码:

(看到有的人用的是一个队列,代码看起来简单些,但原理是一样的,顺便附上数据生成代码)

 

 

#include<iostream>
#include<sstream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>

using namespace std;

int fac[]={1,1,2,6,24,120,720,5040,40320,362880};
int vis1[363000],vis2[363000];
int ed[]={1,2,3,4,5,6,7,8,0};
string path[363000];//记录路径

int cantor(int s[]){//康托展开,进行状态压缩
    int sum=0;
    for(int i=0;i<9;i++){
        int cnt=0;
        for(int j=i+1;j<9;j++)
            if(s[j]<s[i]) cnt++;
        sum+=cnt*fac[9-i-1];
    }
    return sum;
}

struct node{
    int s[9];
    int cur,n;//同A*算法
    int deep;//记录所在层次
}t[2];

int d[][2]={{1,0},{0,-1},{0,1},{-1,0}};
char dr1[]="dlru",dr2[]="urld";//这里前后遍历的方向相反,路径也相反

void bfs(node &t1,node &t2){//可以看出,双BFS的前后代码几乎是对称的,vis两个,队列两个
    memset(vis1,0,sizeof(vis1));
    memset(vis2,0,sizeof(vis2));
    if(t1.n==t2.n) {cout<<0<<endl<<endl;return;}
    queue<node> q,qq;
    q.push(t1);
    vis1[t1.n]=1;
    path[t1.n]="";
    qq.push(t2);
    vis2[t2.n]=1;
    path[t2.n]="";
    int L=0;//记录层次,既然是双向的,肯定是你一层,我一层的顺序依次进行
    while(!q.empty() || !qq.empty()){
        while(q.front().deep==L){
            node tmp=q.front();
            q.pop();
            int x=tmp.cur/3,y=tmp.cur%3;
            for(int p=0;p<4;p++){
                int tx=x+d[p][0],ty=y+d[p][1];
                if(tx<0 || ty<0 || tx>2 || ty>2) continue;
                node tmp2=tmp;
                tmp2.cur=tx*3+ty;
                swap(tmp2.s[tmp.cur],tmp2.s[tmp2.cur]);
                tmp2.n=cantor(tmp2.s);
                tmp2.deep=tmp.deep+1;
                if(vis2[tmp2.n]){//如果找到解,打印路径
                    cout<<(int)path[tmp2.n].size()+(int)path[tmp.n].size()+1<<endl;//打印长度
                    reverse(path[tmp2.n].begin(),path[tmp2.n].end());
                    cout<<path[tmp.n]<<dr1[p]<<path[tmp2.n]<<endl;
                    return;
                }
                if(vis1[tmp2.n]) continue;
                vis1[tmp2.n]=1;
                path[tmp2.n]=path[tmp.n];
                path[tmp2.n]+=dr1[p];
                q.push(tmp2);

            }
        }
        while(qq.front().deep==L){
            node tmp=qq.front();
            qq.pop();
            int x=tmp.cur/3,y=tmp.cur%3;
            for(int p=0;p<4;p++){
                int tx=x+d[p][0],ty=y+d[p][1];
                if(tx<0 || ty<0 || tx>2 || ty>2) continue;
                node tmp2=tmp;
                tmp2.cur=tx*3+ty;
                swap(tmp2.s[tmp.cur],tmp2.s[tmp2.cur]);
                tmp2.n=cantor(tmp2.s);
                tmp2.deep=tmp.deep+1;
                //下面 if 得注意,我们不能像杭电1043那样就打印了,因为我们不能确定这条路径的字典序是最小的,
                //要找到最小的字典序,还得由正向BFS确定,我们这里跳过对其操作,由下一步的正向BFS打印
                if(vis1[tmp2.n])  continue;
                //下面这里的 if 注意,由于是反方向遍历,&&后面的比较必须有,否则WA
                if(vis2[tmp2.n] && path[tmp2.n][(int)path[tmp2.n].size()-1]<dr2[p]) continue;
                vis2[tmp2.n]=1;
                path[tmp2.n]=path[tmp.n];
                path[tmp2.n]+=dr2[p];
                qq.push(tmp2);
            }
        }
        L++;//层次+1
    }
}

int main(){
    //freopen("C:\\Documents and Settings\\All Users\\桌面\\in.txt","r",stdin);
    //freopen("C:\\Documents and Settings\\All Users\\桌面\\out2.txt","w",stdout);
    int T;cin>>T;
    for(int p=1;p<=T;p++)
    {
        char ch;
        for(int j=0;j<2;j++)
        for(int i=0;i<9;i++){
            cin>>ch;
            if(ch=='X') {
                ch='0';
                t[j].cur=i;
            }
            t[j].s[i]=ch-'0';
        }
        t[0].n=cantor(t[0].s);t[0].deep=0;
        t[1].n=cantor(t[1].s);t[1].deep=0;

        printf("Case %d: ",p);
        bfs(t[0],t[1]);
    }
    return 0;
}


/*
数据生成:
#include <iostream>
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <ctime>
#include <vector>
#include <cmath>
#include <cstdlib>
using namespace std;

const int maxn=1e3+10;
int f[maxn],g[maxn];
void random(int t)
{

    int i,j,k,m,num=0;
    for(i=0;i<t;i++)f[i]=i;
    m=t;
    for(i=0;i<t;i++)
    {
        num=rand()%m;
        g[i]=f[num];
        f[num]=f[m-1];
        m--;
    }
}
int judge()
{
    int i,j,k=0;
    for(i=0;i<9;i++)
    {
        //printf("%c\n",e.c[i]);
        if(g[i]==0)continue;
        for(j=0;j<i;j++)
        {
            if(g[j]==0)continue;
            if(g[j]>g[i])k++;
        }
    }
    return k%2;
}
int main()
{
    freopen("C:\\Documents and Settings\\All Users\\桌面\\in.txt","w",stdout);
    srand(time(NULL));
    int i,j,k,n;
    printf("200\n");
    for(i=0;i<200;i++)
    {
        random(9);
        k=judge();
        for(j=0;j<9;j++)
        {
            if(g[j]==0)
                printf("X");
            else
                printf("%d",g[j]);
        }
        printf("\n");
        do
        {
            random(9);
        }while(k!=judge());
        for(j=0;j<9;j++)
        {
            if(g[j]==0)
                printf("X");
            else
                printf("%d",g[j]);
        }
        printf("\n");
    }
}

*/

 

 

 

抱歉!评论已关闭.