分层图(网络流)- 孤岛营救问题(luogu 4011)

版权声明:转载请注明出处 https://blog.csdn.net/weixin_42557561/article/details/86604561

传送门


牢骚写在前面

好久好久没有更过博客了
上一篇板子纯粹是为了自己记住,都没有好好写过题解了
自从考完noip,整个人都颓了,连“退役记”“游记”都没有写

不过,这些都不重要了
我还在这条路上摸爬滚打着
博客还是要更的
就像是日子总要过下去


Analysis

建图好题!
听说状压也可以乱搞,不过我还是乖乖练习一下建图
思考在一张图上,按照常规思路建图,然后跑最短路
如果没有钥匙,显然是可以的,问题是现在我们有钥匙,可以节省一些时间
问题就变成我们该选择拿哪一些钥匙,可以花费最少的时间到达终点

考虑如果拿到一把钥匙,其对应的门就可以直接走过去了
这就好比,拿到一把钥匙就进入了一个新世界,在这个世界里,原来有的路依然在,但由于有钥匙了,还有一些新的路可以被使用

抽象地理解这个了之后,我们就可以引入分层图了
在第0层里是没有任何一把钥匙的
下一层有一把钥匙
这样依次往下
建图细节看代码
最后跑一遍最短路即可
在这里插入图片描述

Code
#include<bits/stdc++.h>
#define in read()
#define int long long
#define re register
using namespace std;
inline int read(){
	char ch;int f=1,res=0;
	while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
	while(ch>='0'&&ch<='9') {
		res=(res<<1)+(res<<3)+(ch^48);
		ch=getchar();
	}
	return f==1?res:-res;
}
const int N=300009;
const int Mx=5000009;
int inf;
bool vis[N];
int n,m,k,d[N],s,g[200][200],keyn,M;
vector<pair<int,int> > V[200]; 
int nxt[Mx],to[Mx],head[N],w[Mx],ecnt=0;
inline void add(int x,int y,int z){	nxt[++ecnt]=head[x];head[x]=ecnt;to[ecnt]=y;w[ecnt]=z;}
inline int num(int x,int y){	return (x-1)*m+y;}
int layer;
inline void build(){
	for(re int p=0;p<=layer;++p){
		for(re int q=1;q<=keyn;++q)
			vis[q]=p&(1<<q-1);
		for(re int i=1;i<=n;++i)
			for(re int j=1;j<=m;++j){
				int x=num(i,j),y=num(i,j+1);
				if(j<m&&g[x][y]!=-1) {
					if(g[x][y]==0||vis[g[x][y]])
						add(M*p+x,M*p+y,1),add(M*p+y,M*p+x,1);
				}
				y=num(i+1,j);
				if(i<n&&g[x][y]!=-1){
					if(g[x][y]==0||vis[g[x][y]])
						add(M*p+x,M*p+y,1),add(M*p+y,M*p+x,1);
				}
			}
		for(re int q=1;q<=keyn;++q){
			if(!vis[q]){
				int tmp=p+(1<<q-1);
				for(re int i=0;i<V[q].size();++i){
					int x=num(V[q][i].first,V[q][i].second);
					add(M*p+x,M*tmp+x,0);
				}
			}
		}
	}
}
inline void dij(){
	priority_queue<pair<int,int> > q;
	memset(d,127/3,sizeof(d));
	inf=d[1];
	d[1]=0;q.push(make_pair(0,1));
	memset(vis,0,sizeof(vis));
	while(!q.empty()){
		int u=q.top().second;q.pop();
		if(vis[u]) continue;
		vis[u]=1;
		for(re int e=head[u];e;e=nxt[e]){
			int v=to[e];
			if(d[v]>d[u]+w[e]){
				d[v]=d[u]+w[e];
				q.push(make_pair(-d[v],v));
			}
		}
	}
}
signed main(){
	n=in;m=in;keyn=in;k=in;int xx,yy,xxx,yyy,ch;
	M=n*m;layer=(1<<keyn)-1;
	for(re int i=1;i<=k;++i){
		xx=in;yy=in;xxx=in;yyy=in;ch=in;
		int x=num(xx,yy),y=num(xxx,yyy);
		if(ch>=1) g[x][y]=ch;//door
		if(ch==0) g[x][y]=-1;//wall
		//默认情况=0 则表示无门无墙 
	}
	s=in;
	for(re int i=1;i<=s;++i){
		xx=in;yy=in;ch=in;
		V[ch].push_back(make_pair(xx,yy));
	}
	build();
	dij();
	int ans=inf;
	for(re int i=0;i<=layer;++i) ans=min(ans,d[i*M+M]);
	if(ans==inf) cout<<-1;
	else cout<<ans;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_42557561/article/details/86604561