孤岛营救问题/拯救大兵瑞恩(BFS+状态压缩)

原题见洛谷。

分析

这题其实可以分层建图,然后跑SFPA,但单纯BFS代码量明显小得多。

先看数据范围:最大为10,所以空间可以随便搞。

开一个mp[11][11][11][11],表示(x1,y1),(x2,y2)之间是否有什么东西,然后开一个key[11][11][16]存(x,y)放在的钥匙编号,开一个cnt[11][11]记录(x,y)钥匙数量,开一个vis[11][11][2100]记录是否访问到。

这题特点(坑点)有以下几点:

1,一类钥匙一把可以重复使用,所以状压还是很方便。

2,输入mp和BFS时容易忽略两点间什么都没有的情况。

3,开vis数组时可以大胆点开大一些。

4,输出-1。

#include<bits/stdc++.h>
using namespace std;
int N,M,P,K,S,vis[11][11][2100],mp[11][11][11][11],key[11][11][16],cnt[11][11],dx[4]={0,0,-1,1},dy[4]={-1,1,0,0};
struct data{int x,y,step,state;}; queue<data>q;

char c;
void scan(int &x)
{
	for(c=getchar();c<'0'||c>'9';c=getchar());
	for(x=0;c>='0'&&c<='9';c=getchar()) x=x*10+c-'0';
}

int BFS()
{
	data t;
	int now=0,now_x,now_y,now_state,now_step,t_x,t_y,t_state,k;
	for(int i=1;i<=cnt[1][1];i++) now=now|(1<<(key[1][1][i]));
	q.push((data){1,1,0,now}); vis[1][1][now]=1;
	while(!q.empty())
	{
		t=q.front(); q.pop();
		now_x=t.x,now_y=t.y,now_state=t.state,now_step=t.step; //便于操作 
		
		for(int i=0;i<=3;i++)
		{
			t_x=t.x+dx[i],t_y=t.y+dy[i];
			
			if(t_x<1||t_x>N||t_y<1||t_y>M) continue; //越界 
			
			k=mp[now_x][now_y][t_x][t_y];
			if(k==-1) continue; //有墙 
			if(k&&!(now_state&(1<<k))) continue; //有锁,没钥匙 
			
			if(t_x==N&&t_y==M) return now_step+1; //这个格子可以到了,先看是不是终点 
			
			t_state=now_state; //不是终点,先把这个点的钥匙拿走 
			for(int j=1;j<=cnt[t_x][t_y];j++) t_state=t_state|(1<<(key[t_x][t_y][j]));
			
			if(vis[t_x][t_y][t_state]) continue; //访问过 
			vis[t_x][t_y][t_state]=1;
			q.push((data){t_x,t_y,now_step+1,t_state}); 
		}
	}
	return -1;
}

int main()
{
	int i,x1,x2,y1,y2,op;
	scan(N);scan(M);scan(P);scan(K);
	for(i=1;i<=K;i++)
	{
		scan(x1);scan(y1);scan(x2);scan(y2);scan(op);
		mp[x1][y1][x2][y2]=mp[x2][y2][x1][y1]=op?op:-1;
	} scan(S);
	for(i=1;i<=S;i++)
	{
		scan(x1);scan(y1);scan(op);
		cnt[x1][y1]++; key[x1][y1][cnt[x1][y1]]=op;
	} cout<<BFS(); return 0;
}

 

猜你喜欢

转载自blog.csdn.net/WWWengine/article/details/81781854