BZOJ1054[HAOI]移动玩具|状态压缩SPFA

Description

在一个4*4的方框内摆放了若干个相同的玩具,某人想将这些玩具重新摆放成为他心中理想的状态,规定移动时只能将玩具向上下左右四个方向移动,并且移动的位置不能有玩具,请你用最少的移动次数将初始的玩具状态移动到某人心中的目标状态。

Input

前4行表示玩具的初始状态,每行4个数字1或0,1表示方格中放置了玩具,0表示没有放置玩具。接着是一个空行。接下来4行表示玩具的目标状态,每行4个数字1或0,意义同上。

Output

一个整数,所需要的最少移动次数。

Sample Input

1111
0000
1110
0010

1010
0101
1010
0101

Sample Output

4





题解请见代码!

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>

using namespace std;

bool vis[1<<17];
int st,en,num,tot,MAXN;
int shu[1<<17],dis[1<<17],h[1<<17];
char s[6];

struct Edge{
	int u,v,w,next;
}e[1001000];

void add(int u,int v,int w) {
	e[++tot].u=u;
	e[tot].v=v;
	e[tot].w=w;
	e[tot].next=h[u];
	h[u]=tot;
}

void spfa() {//spfa
	queue<int> q;
	memset(dis,0x3f,sizeof(dis));
	vis[st]=1;
	dis[st]=0;
	q.push(st);
	while(!q.empty()) {
		int u=q.front();
		q.pop();
		vis[u]=0;
		for(int i=h[u];i;i=e[i].next) {
			int v=e[i].v;
			if(dis[v]>dis[u]+e[i].w) {
				dis[v]=dis[u]+e[i].w;
				if(!vis[v]) {
					q.push(v);
					vis[v]=1;
				}
			}
		}
	}
}

void init(){
	int i,j;
	for(i=0;i<4;++i) {
		scanf("%s",s);
		for(j=0;j<4;++j){
			if(s[j]=='1'){
				num++;//总共1数
				st+=1<<(i*4+j);//st为初始状态数
			}
		}
	}
	getchar();
	for(i=0;i<4;++i) {
		scanf("%s",s);
		for(j=0;j<4;++j){
			if(s[j]=='1') {
				en+=1<<(i*4+j);//en为目标状态数
			}
		}
	}
	for(i=0;i<16;++i) shu[1<<i]=i+1;//给每个状态标号
	MAXN=(1<<16);tot=0;
	for(i=0;i<MAXN;++i) {
		int numi=0,k=i;
		while(k) {
			++numi;
			k-=(k&(-k));
		}
		if(numi==num) {//如果枚举到的i状态与num相同就建图
			for(j=i;j;j-=(j&(-j))) {//枚举合法状态每个1的位置
				if(shu[j&(-j)]%4!=1){//如果这个1在位置数组里%4!=1就表示它不在最左边
					int l=(j&(-j))>>1;//可以右移一位,l为移动后的状态
					if(!(l&i)){//如果状态合法,即在l与i中没有两个相同位置都是1
						add(i,i-(j&(-j))+l,1);//建边
					}
				}
				if(shu[j&(-j)]%4!=0){//如果这个1在位置数组里%4!=0就表示它不在最右边
					int l=(j&(-j))<<1;
					if(!(l&i)) {
						add(i,i-(j&(-j))+l,1);
					}
				}
				if(shu[j&(-j)]/4<3){如果这个1在位置数组里/4!<3就表示它不在最下边
					int l=(j&(-j))<<4;//往上走
					if(!(l&i)) {
						add(i,i-(j&(-j))+l,1);
					}
				}
				if(shu[j&(-j)]/4!=0) {//如果这个1在位置数组里/4!=0就表示它不在最上边
					int l=(j&(-j))>>4;//往下走
					if(!(l&i)) {
						add(i,i-(j&(-j))+l,1);
					}
				}
			}
		}
	}
	spfa();
	printf("%d",dis[en]);
}

int main(){
	init();
//	while(1);
}
/*
1111
0000
1110
0010

1010
0101
1010
0101
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值