洛谷P4011 【网络流24题】 孤岛营救问题 (BFS+状压)

一道妙题啊......(不知道为什么这道题的标签是网络流,不需要用网络流啊)

如果没有门和钥匙,连边(边权为1)求最短路就行了。

但是有这两个因素的限制,我们采用分层建图的思想,一共2p层,每层对应持有钥匙的2p种状态(就是状态压缩),在分层图上连边,当前层没有的钥匙,就向有该类钥匙的层连边(注意此时的边权是0)。最后宽搜求最短路就行了,答案是每层图终点取最小值。

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 const int N=1e6;
  4 int first[N],to[N],w[N],nxt[N],tot;
  5 struct node{
  6     int x,y;
  7 }key[15][20];//存钥匙
  8 int n,M,row,line,keyn;
  9 int layer,r,num[20][20];
 10 int fg[200][200],kn[15],dis[102500];
 11 int q[N],inf=0x3f3f3f3f;
 12 bool vis[102500];
 13 
 14 void add(int x,int y,int z){
 15     nxt[++tot]=first[x];first[x]=tot;
 16     to[tot]=y;w[tot]=z;
 17 } 
 18 
 19 void build(){
 20     int i,j,k,x,y,t;
 21     bool havekey[15]={0};
 22     M=row*line;//每层节点数
 23     layer=1<<keyn;//总层数
 24     n=layer*M;//总结点数
 25     for(k=0;k<layer;k++){//对每层处理 
 26         for(i=1;i<=keyn;i++)
 27              if(k&(1<<(i-1))) havekey[i]=true;
 28              else havekey[i]=false;//该层有哪几类钥匙
 29          for(i=1;i<=row;i++)
 30          for(j=1;j<=line;j++){
 31              x=num[i][j];y=num[i][j+1];//向右连边 
 32              if(y!=0&&fg[x][y]!=-1)
 33              if(fg[x][y]==0||havekey[fg[x][y]]){
 34                  add(k*M+x,k*M+y,1);
 35                  add(k*M+y,k*M+x,1);
 36             }
 37             y=num[i+1][j];//向左连边 
 38             if(y!=0&&fg[x][y]!=-1)
 39             if(fg[x][y]==0||havekey[fg[x][y]]){
 40                 add(k*M+x,k*M+y,1);
 41                 add(k*M+y,k*M+x,1);
 42             }
 43         }
 44         for(i=1;i<=keyn;i++)//当前层没有钥匙,转移到有该类钥匙的层 
 45         if(!havekey[i]){
 46             t=k+(1<<(i-1));//t表示有第i类钥匙的层 
 47             for(j=1;j<=kn[i];j++){
 48                 x=num[key[i][j].x][key[i][j].y];
 49                 add(k*M+x,t*M+x,0);//注意连边的权值是0 
 50             }
 51         }
 52     }
 53 } 
 54 
 55 void Read(){
 56     int i,j,k,x,y,p;
 57     cin>>row>>line>>keyn>>r;k=0;
 58     for(i=1;i<=row;i++)
 59         for(j=1;j<=line;j++) num[i][j]=++k;//编号
 60     for(i=1;i<=r;i++){
 61         scanf("%d%d",&x,&y);j=num[x][y];
 62         scanf("%d%d",&x,&y);k=num[x][y];
 63         cin>>p;if(p==0) p=-1;//表示墙 
 64         fg[j][k]=fg[k][j]=p;//有门/墙 
 65     } 
 66     cin>>r;
 67     for(i=1;i<=r;i++){
 68         scanf("%d%d%d",&x,&y,&p);
 69         kn[p]++;
 70         key[p][kn[p]].x=x;
 71         key[p][kn[p]].y=y;//第p类钥匙的第kn[p]把的位置 
 72     }
 73 }
 74 
 75 void SPFA(){
 76     int i,j,k,head,tail;
 77     for(i=1;i<=n;i++) dis[i]=inf;
 78     dis[1]=0,vis[1]=true,q[1]=1;
 79     head=tail=1;
 80     while(head<=tail){
 81         i=q[head];
 82         for(k=first[i];k;k=nxt[k]){
 83             j=to[k];
 84             if(dis[j]>dis[i]+w[k]){
 85                 dis[j]=dis[i]+w[k];
 86                 if(!vis[j]){
 87                     q[++tail]=j;vis[j]=true;
 88                 }
 89             }
 90         }
 91         vis[i]=false,head++;
 92     }
 93 }
 94 
 95 void solve(){
 96     int i,ans=inf,T;
 97     SPFA();
 98     T=num[row][line];
 99     for(i=0;i<layer;i++)
100         ans=min(ans,dis[i*M+T]);
101     if(ans==inf) cout<<-1<<endl;
102     else cout<<ans<<endl;
103 } 
104 
105 int main(){
106     Read();//读入数据 
107     build();//建图 
108     solve();
109     return 0;
110 }

重点还是在于建图。。。

posted @ 2022-04-05 11:03  YHXo  阅读(44)  评论(0编辑  收藏  举报