孤岛营救问题

https://loj.ac/problem/6121

 题目描述

  有一张n×m的网格图,格与格之间可能有门和墙,墙不可经过,门必须有相应的钥匙,求从左上角到右下角的最短路径。

思路

  虽然这是网络流24题,但实际上我们可以用更简单的思路——分层图来做。我们考虑p比较小,也就是钥匙的种类比较少,可以状态压缩后有一个数表示所用的钥匙个数,我们据此可以建成一张(1<<k)层的分层图,每一层对应拥有钥匙的一种状态,所以我们可以把每一层的基本图建成,再处理一下层之间的连边,跑一遍spfa求最短路即可。

代码

#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10,INF=100000000;
struct aa
{
    int x,y;
    aa(int x=0,int y=0):x(x),y(y) {}
};
vector<aa>key[20];
struct Edge
{
    int to,nxt,w;
}e[N];
int head[N],tot,num[20][20],mp[200][200];
int keyn,m,n,p,l,dis[N],exist[N],k;
void add_edge(int x,int y,int v)
{
    e[++tot].nxt=head[x];head[x]=tot;
    e[tot].to=y;e[tot].w=v;
}
void build()
{
    l=(1<<p);
    int s=m*n;
    int have_key[20]={0};    //有那几把钥匙 
    for(int k=0;k<l;k++)
    {
        for(int i=1;i<=p;i++)
            if(k&(1<<i-1))have_key[i]=1;    //把状压传化过来 
            else have_key[i]=0;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
            {
                int x=num[i][j],y=num[i][j+1];        //向右走 
                if(y!=0&&mp[x][y]!=-1)
                {
                    if(mp[x][y]==0||have_key[mp[x][y]])
                    {
                        add_edge(k*s+x,k*s+y,1);
                        add_edge(k*s+y,k*s+x,1);
                    }
                }
                y=num[i+1][j];                        //向下走 
                if(y!=0&&mp[x][y]!=-1)
                {
                    if(mp[x][y]==0||have_key[mp[x][y]])
                    {
                        add_edge(k*s+x,k*s+y,1);
                        add_edge(k*s+y,k*s+x,1);
                    }
                }
            }
        for(int i=1;i<=p;i++)
            if(!have_key[i])
            {
                int goal=k+(1<<i-1);
                for(int j=0;j<key[i].size();j++)
                {
                    int x=num[key[i][j].x][key[i][j].y];
                    add_edge(k*s+x,goal*s+x,0);            //层之间的连边 
                }
            }
    }
}
void spfa()
{
    queue<int>q;
    for(int i=1;i<=m*n*l;i++)    
        dis[i]=INF;
    memset(exist,0,sizeof(exist));
    dis[1]=0;q.push(1);exist[1]=1;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=head[u];i;i=e[i].nxt)
        {
            int v=e[i].to;
            if(dis[v]>dis[u]+e[i].w)
            {
                dis[v]=dis[u]+e[i].w;
                if(!exist[v])
                {
                    q.push(v);
                    exist[v]=1;
                }
            }
        }
        exist[u]=0;
    }
}
int main() 
{
    int cnt=0;
    scanf("%d%d%d%d",&n,&m,&p,&k);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            num[i][j]=++cnt;
    for(int i=1;i<=k;i++)
    {
        int x1,x2,y1,y2,g;
        scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&g);
        if(g==0)g=-1;
        int a=num[x1][y1],b=num[x2][y2];
        mp[a][b]=mp[b][a]=g;
    }
    scanf("%d",&keyn);
    for(int i=1;i<=keyn;i++)
    {
        int x,y,q;
        scanf("%d%d%d",&x,&y,&q);
        key[q].push_back(aa(x,y));
    }
/*    for(int i=1;i<=p;i++)
    {
        printf("%d\n",i);
        for(int j=0;j<key[i].size();j++)
            printf("%d %d\n",key[i][j].x,key[i][j].y);
        printf("\n");
    }*/
    build();
    spfa();
    int ans=INF;
    for(int i=0;i<l;i++)
        ans=min(ans,dis[i*m*n+num[n][m]]);
    if(ans<INF)printf("%d",ans);
    else printf("-1");
    return 0;
}

 

 

 

 

  

posted @ 2019-10-15 17:23  fbz  阅读(177)  评论(0编辑  收藏  举报