POJ 1077 Eight(康拓展开 BFS 双向BFS)

题目:http://poj.org/problem?id=1077

题目大意:给你一个3*3的矩阵,有数字1~8,还有一个x,x能往周围移动,一移动会与目标格子的数字交换。求出能把矩阵变成1 2 3 / 4 5 6 / 7 8 x 的一个方案,如果不行,就输出“unsolvable”。

解题思路:很不错的搜索题!实现真的比较麻烦。首先,广搜,要保存每个节点的状态,状态还要判重,所以这里要hash。这里是数字的排列,那么用康拓展开来弄。还有就是记录路径,我用的是直接在节点里用string来完全记录路径,也可以开数组村每个状态的上一个状态和转到他的方向,相对来说是后面一种比较快。

这道题目,我是用裸BFS做的,话说可以双向BFS,A*,IDA*。双向BFS见下文。A*和IDA*还没学,学了再来。。

裸BFS代码如下:(写的比较挫,C++ TLE,G++ AC,估计就会记录路径那里每次都要复制路径,比较耗时  = = )

#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
#include<iostream>
using namespace std;

const int MAXN = 12;//int 最多 12!

struct Cantor
{
	int n,k;
	int fac[MAXN],hh[MAXN];//hh为标记数组
	void init(int n)
	{
		this->n = n;
		fac[0] = 1;
        for(int i = 1;i <= MAXN;i++)
            fac[i] = fac[i-1]*i;
	}

	int code(int* s)//康拓展开
	{
		int num = 0;
		for(int i = 0;i < n;i++)
		{
			int cnt = 0;
			for(int j = i+1;j < n;j++)
				if(s[i] > s[j]) cnt++;//求逆序数
			num += cnt*fac[n-1-i];
		}
		return num+1;
	}

	void decode(int k)//逆康拓展开
	{
	    k -= 1;//第k大,那么比他小的有k-1个
	    memset(hh,0,sizeof(hh));
		int a = k,b;
		for(int i = n-1;i >= 0;i--)
		{
			b = a/fac[i];
			a = a%fac[i];
			int cnt = 0;
			int j;
			for(j = 1;cnt < b && j <= n;j++)
				if(hh[j] == 0)
					cnt++;
			while(hh[j] == 1) j++;
			hh[j] = 1;
			printf("%d ",j);//打印的序列从 1 开始
		}
		puts("");
	}
}ct;

int s[11];

struct Node
{
    int x,y,st;
    int ss[11];
    string path;
};

bool vis[444444];
int dir_x[] = {-1,0,1,0},dir_y[] = {0,1,0,-1};
char index[] = {'u','r','d','l'};
queue<Node> q;

int ok = 0;

void bfs(int s_x,int s_y,int start)
{
    memset(vis,0,sizeof(vis));
    while(!q.empty()) q.pop();
    Node cur;
    cur.x = s_x;cur.y = s_y;cur.st = start;
    for(int i = 0;i < 9;i++) cur.ss[i] = s[i];
    q.push(cur);
    vis[start] = 1;
    while(!q.empty())
    {
        Node cur = q.front();q.pop();
        if(cur.st == 1)
        {
            cout<<cur.path<<endl;
            ok = 1;
            break;
        }
        for(int dd = 0;dd < 4;dd++)
        {
            int xx = cur.x+dir_x[dd];
            int yy = cur.y+dir_y[dd];
            if(xx < 0 || xx > 2 || yy < 0 || yy > 2) continue;
            Node next = cur;
            next.ss[xx*3+yy] = 9;
            next.ss[cur.x*3+cur.y] = cur.ss[xx*3+yy];
            next.x = xx;next.y = yy;
            next.st = ct.code(next.ss);
            //for(int i = 0;i < 9;i++) printf("%d ",next.ss[i]);
                //puts("");
            if(!vis[next.st])
            {
                next.path += index[dd];
                q.push(next);
                vis[next.st] = 1;
            }
        }
    }
}

int main()
{
    ct.init(9);
    char str[11];
    int s_x,s_y;
    for(int i = 0;i < 9;i++)
    {
        scanf("%s",str);
        if(str[0] == 'x')
        {
            s_x = i/3;
            s_y = i%3;
            s[i] = 9;
        }
        else s[i] = str[0]-'0';
    }
    int start = ct.code(s);
    ok = 0;
    bfs(s_x,s_y,start);
    if(!ok) cout<<"unsolvable"<<endl;
	return 0;
}

/*
1 2 3 x 4 6 7 5 8
rdr

*/


由于起点和终点都很清楚,那么可以用双向BFS,效率会高很多。

双向BFS其实和BFS一样,就是代码量多了一倍。。

关于双向BFS,有一点需要说明,不是两边点交替,而是两边层交替,点交替求出来可能不是最短路径。这里我的实现方法是0、1交替来标记不同的层。

在路径输出方面,这次我用了数组标记的方法,即标记每个状态是由哪个状态得到的,后面用递归输出。

双向BFS代码如下:(50ms以内了,效率果然高了很多)

#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
#include<iostream>
using namespace std;

const int MAXN = 12;//int 最多 12!

struct Cantor
{
	int n,k;
	int fac[MAXN],hh[MAXN];//hh为标记数组
	void init(int n)
	{
		this->n = n;
		fac[0] = 1;
        for(int i = 1;i <= MAXN;i++)
            fac[i] = fac[i-1]*i;
	}

	int code(int* s)//康拓展开
	{
		int num = 0;
		for(int i = 0;i < n;i++)
		{
			int cnt = 0;
			for(int j = i+1;j < n;j++)
				if(s[i] > s[j]) cnt++;//求逆序数
			num += cnt*fac[n-1-i];
		}
		return num+1;
	}

	void decode(int k)//逆康拓展开
	{
	    k -= 1;//第k大,那么比他小的有k-1个
	    memset(hh,0,sizeof(hh));
		int a = k,b;
		for(int i = n-1;i >= 0;i--)
		{
			b = a/fac[i];
			a = a%fac[i];
			int cnt = 0;
			int j;
			for(j = 1;cnt < b && j <= n;j++)
				if(hh[j] == 0)
					cnt++;
			while(hh[j] == 1) j++;
			hh[j] = 1;
			printf("%d ",j);//打印的序列从 1 开始
		}
		puts("");
	}
}ct;

int s[11];

typedef pair<int,char> pr;

pr path1[444444],path2[444444];

struct Node
{
    int x,y,st;
    int ss[11];
    int flag;
};

bool vis1[444444],vis2[444444];
int dir_x[] = {-1,0,1,0},dir_y[] = {0,1,0,-1};
char index1[] = {'u','r','d','l'},index2[] = {'d','l','u','r'};
queue<Node> q1,q2;

int start,ed;

void print_path1(int st)
{
    if(st == start)
    {
        return ;
    }
    print_path1(path1[st].first);
    printf("%c",path1[st].second);
}

void print_path2(int st)
{
    if(st == ed)
    {
        printf("%c",path2[st].second);
        return ;
    }
    printf("%c",path2[st].second);
    print_path2(path2[st].first);

}

int bfs1(int flag)
{
    while(!q1.empty() && q1.front().flag != flag)
    {
        Node cur = q1.front();q1.pop();
        for(int dd = 0;dd < 4;dd++)
        {
            int xx = cur.x+dir_x[dd];
            int yy = cur.y+dir_y[dd];
            if(xx < 0 || xx > 2 || yy < 0 || yy > 2) continue;
            Node next = cur;
            next.ss[xx*3+yy] = 9;
            next.ss[cur.x*3+cur.y] = cur.ss[xx*3+yy];
            next.x = xx;next.y = yy;
            next.st = ct.code(next.ss);

            if(vis2[next.st])
            {
                print_path1(cur.st);
                printf("%c",index1[dd]);
                print_path2(next.st);
                puts("");
                return 1;
            }
            if(!vis1[next.st])
            {
                path1[next.st] = make_pair(cur.st,index1[dd]);
                next.flag = flag;
                q1.push(next);
                vis1[next.st] = 1;
            }
        }
    }
    return 0;
}

int bfs2(int flag)
{
    while(!q2.empty() && q2.front().flag != flag)
    {
        Node cur = q2.front();q2.pop();
        for(int dd = 0;dd < 4;dd++)
        {
            int xx = cur.x+dir_x[dd];
            int yy = cur.y+dir_y[dd];
            if(xx < 0 || xx > 2 || yy < 0 || yy > 2) continue;
            Node next = cur;
            next.ss[xx*3+yy] = 9;
            next.ss[cur.x*3+cur.y] = cur.ss[xx*3+yy];
            next.x = xx;next.y = yy;
            next.st = ct.code(next.ss);

            if(vis1[next.st])
            {
                print_path1(next.st);
                printf("%c",index2[dd]);
                print_path2(cur.st);
                puts("");
                return 1;
            }
            if(!vis2[next.st])
            {
                path2[next.st] = make_pair(cur.st,index2[dd]);
                next.flag = flag;
                q2.push(next);
                vis2[next.st] = 1;
            }
        }
    }
    return 0;
}

int bibfs(int s_x,int s_y,int e_x,int e_y)
{
    if(start == ed) return 1;
    memset(vis1,0,sizeof(vis1));
    while(!q1.empty()) q1.pop();
    Node cur;
    cur.x = s_x;cur.y = s_y;cur.st = start;
    for(int i = 0;i < 9;i++) cur.ss[i] = s[i];
    cur.flag = 0;
    q1.push(cur);
    vis1[start] = 1;

    memset(vis2,0,sizeof(vis2));
    while(!q2.empty()) q2.pop();
    cur.x = e_x;cur.y = e_y;cur.st = ed;
    for(int i = 0;i < 9;i++) cur.ss[i] = i+1;
    cur.flag = 0;
    q2.push(cur);
    vis2[ed] = 1;

    int flag1 = 0,flag2 = 0;
    while(!q1.empty() && !q2.empty())
    {
        if(q1.size() < q2.size())
        {
            if(bfs1(flag1 ^= 1)) return 1;
        }
        else
        {
            if(bfs2(flag2 ^= 1)) return 1;
        }
    }
    return 0;
}

int main()
{
    ct.init(9);
    char str[11];
    int s_x,s_y;
    for(int i = 0;i < 9;i++)
    {
        scanf("%s",str);
        if(str[0] == 'x')
        {
            s_x = i/3;
            s_y = i%3;
            s[i] = 9;
        }
        else s[i] = str[0]-'0';
    }
    start = ct.code(s);
    ed = 1;
    if(!bibfs(s_x,s_y,2,2))
        cout<<"unsolvable"<<endl;
	return 0;
}

/*
1 2 3 x 4 6 7 5 8
rdr

*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值