Luogu4011 孤岛营救问题(拯救大兵瑞恩)分层图+最短路

题面:孤岛营救问题

如果没有钥匙,那么这题就是一个最简单的最短路,有了钥匙以后,就要朝这个方向想(一个很经典的套路):能不能对图做某些操作,使得问题再次变成一个简单的最短路问题。然后就要用到一个技巧:分层图。我们共建 2p 2 p 层图,每层图的层数表示当前拥有什么种类的钥匙(用二进制来表示),我们先建立每一层内的边,假设拥有层号代表的这些钥匙,由当前点向能走到的上下左右建立一条边权为1的边。然后建立层外的边,若当前节点有钥匙,则向代表有当前层有的钥匙+现在找到的钥匙层建边,边权为0(捡钥匙不需要时间0)然后跑一边最短路。
不得不说细节很多
代码:

#include
#include
#include
#include
#include
#include
#define INF 0x3f3f3f3f
#define for_edge(i,x) for(int i = Head[x];i!=-1;i = Edges[i].Next)
#define File(s) freopen(#s".in","r",stdin);freopen(#s".out","w",stdout)
#define gi get_int()
int get_int()
{
    int x = 0,y = 1;char ch = getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch = getchar();
    if(ch=='-')y = -1,ch = getchar();
    while(ch<='9'&&ch>='0')x = x*10+ch-'0',ch = getchar();
    return x*y;
}

int Map[1001][1001],Keys[1001];
int n,m,k;

class Edge
{
public:
    int Next,To,Value;
}Edges[1000005];
int E_num,Head[1000005];
void Add_edge(int From,int To,int Value)
{
//  if(!((To+1)%(n*m)))std::cout<
    Edges[E_num] = (Edge){Head[From],To,Value};
    Head[From] = E_num++;
}
bool Can(int x,int q){return x&(1<<(q-1));}
bool Judge(int x,int y,int Num)
{
    if(x<0||y<0||x>=n*m||y>=n*m)return false;
    if(Map[x][y]==-1)return true;
    if(Map[x][y]==0)return false;
    if(Map[x][y]>=1&&Can(Num,Map[x][y]))return true;
    return false;
}
void Build_Inside(int x,int Num)
{
    for(int i = 0;iif(Judge(i,i+m,x)==true)
            Add_edge(i+Num*n*m,(i+m)+Num*n*m,1);
        if(Judge(i,i-m,x)==true)
            Add_edge(i+Num*n*m,(i-m)+Num*n*m,1);
        if(((i+1)%m)!=0&&Judge(i,i+1,x)==true)
            Add_edge(i+Num*n*m,(i+1)+Num*n*m,1);
        if(((i+1)%m)!=1&&Judge(i,i-1,x)==true)
            Add_edge(i+Num*n*m,(i-1)+Num*n*m,1);
    }
}

void Build_Outside(int Key_num)
{
    for(int i = 0;ifor(int j = i*n*m;j<(i+1)*n*m;j++)
            if(Keys[j-(i*n*m)]!=0&&(i&Keys[j-(i*n*m)])==0)Add_edge(j,(j+(n*m*Keys[j-(i*n*m)])),0);
}

int Dist[120000],Queue[1000000];
bool Vis[120000];

void SPFA()
{
    std::queue<int> Q;
    memset(Dist,0x3f,sizeof(Dist));
    Dist[0] = 0;
    Vis[0] = true;
    Q.push(0);
    while(!Q.empty())
    {
        int Now = Q.front();Q.pop();
//      if(!((Now+1)%(n*m)))std::cout<<"hello"<
        Vis[Now] = false;
        for_edge(i,Now)
        {
            int v = Edges[i].To,w =Edges[i].Value;
            if(Dist[Now]+w>=Dist[v])continue;
            Dist[v] = Dist[Now]+w;
            if(Vis[v]==false)
            {
                Vis[v] = true;
                Q.push(v);
            }
        }
    }
}

int main()
{
    n = gi,m = gi;
    int p = gi;
    k = gi;
    memset(Map,-1,sizeof(Map));
    memset(Head,-1,sizeof(Head));
    for(int i = 0;iint x1 = gi-1,y1 = gi-1,x2 = gi-1,y2 = gi-1,G = gi;
        Map[x1*m+y1][x2*m+y2] = Map[x2*m+y2][x1*m+y1] = G;
    }
    int s = gi;
    for(int i = 0;iint x = gi-1,y = gi-1,q = gi;
        Keys[x*m+y] |= 1<<(q-1);
    }
    int Key_num = 1<for(int i = 0;iint Ans = INF;
    for(int i = 0;istd::min(Ans,Dist[((i+1)*n*m)-1]);
    if(Ans==INF)std::cout<<-1;
    else std::cout<return 0;
}

你可能感兴趣的:(题解-总结,图论)