zoukankan      html  css  js  c++  java
  • 孤岛营救问题

    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
    ",i);
            for(int j=0;j<key[i].size();j++)
                printf("%d %d
    ",key[i][j].x,key[i][j].y);
            printf("
    ");
        }*/
        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;
    }

      

  • 相关阅读:
    【BZOJ2599】[IOI2011]Race 树的点分治
    【BZOJ1787】[Ahoi2008]Meet 紧急集合 LCA
    【BZOJ1834】[ZJOI2010]network 网络扩容 最大流+最小费用流
    【BZOJ3012】[Usaco2012 Dec]First! Trie树+拓补排序
    【BZOJ2743】[HEOI2012]采花 离线+树状数组
    【BZOJ2946】[Poi2000]公共串 后缀数组+二分
    【BZOJ2157】旅游 树链剖分+线段树
    【BZOJ2661】[BeiJing wc2012]连连看 最大费用流
    【BZOJ1801】[Ahoi2009]chess 中国象棋 DP
    【BZOJ4236】JOIOJI STL
  • 原文地址:https://www.cnblogs.com/fangbozhen/p/11674223.html
Copyright © 2011-2022 走看看