拯救大兵瑞恩

原题链接

分析

知识点

拆点图

思路分析

从原题中,我们要求的是从最上角走到最下角的最小步数。

但是此时加了一些限制,想通过门,需要相应的钥匙才能通过

那么相同下标的点,可能就因为此时是否有对应钥匙而变得不同。

这就提示我们,可以用拆点的方法,来把相同的下标的点,根据不同的状态分开,就是拆点。

此时不同的状态,就是拥有的钥匙种类

因此此题的解决策略即为:

  1. 根据门,通路,墙进行连边操作。
  2. 记录下来,每个点所拥有的钥匙
  3. 接下来就是跑一边最短路算法,就能得到到达(n,m)的最短距离。

更加细致的分析和二刷体悟

其实,第一次过这个题目的时候,并没有想得太多,因为拆点图这个知识点对我而言就很新鲜了,根本没有时间去想别的细节了。

但这次二刷的时候发现了很多当时被拆点图这个知识点所掩盖住的细节处理。所以这里要特意提出来积累一下。

  1. 首先,我们来说一说,处理点与点之间连边的操作。我刚开始想的是,建立邻接矩阵,g[N] [N] [M],以此来表现不同的点,但接下来连接边的时候,我发现这样不行,因为无法连接横跨门的边。所以我修改了一下思路,将二维变为一维,使用邻接表来实现点与点之间的连接,并记录下来该条边是否需要钥匙,需要的是哪把钥匙。此时g[N] [N]记录的就是每个点对应的一维下标。、
  2. 其实这里我想说一下,刚开始我有点发愁,因为地图是从(1,1)开始的,所以我觉得根本无法实现转化,或者说转化了也将非常麻烦。但其实并不是,因为我担心的是如何从一维转回二维去,这一步会很麻烦,不过这一步根本不会发生,所以这个担心是多余的。并且之后我想了一下,就算不是从(0,0)开始,也可以很轻松的从一维变为二维。这里给自己定死一点从二维转一维时,不论是否从(0,0)开始,我们都是扫描一遍,从1开始编号
  3. 接下来,又遇见了另外一个问题,题目中只给了墙和门的边,通路的边,需要我们自己去连接。思路很简单,就是扫描一遍,看看哪些边遇见过,这里有一个小技巧,直接存在set里面,可以直接完成去重的工作,且可以很快的找到,该条是否存在过。
  4. 完成这一步之后,我们需要存储每个点所拥有的钥匙种类。这里的钥匙种类不多,我们可以直接用二进制来优化,用二进制数来表示钥匙的有无。但是若是要是非常多呢?那这题可能就不能写了,因为此时拆点将会变成一个非常困难的事情,且时间复杂度也会非常恐怖。不过或许能用map来映射表示钥匙的有无。
  5. 最后一步优化比较好想,每到一个格子,如果有钥匙,那我们一定要捡起来。

ACcode

/*
1.根据不同的边建边。
2.将每个格子所拥有的钥匙记录。
3.将每条边需要的钥匙进行记录
*/
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
#define x first
#define y second
const int N = 11,M = N*N,E = 400,P = 1<<10;
int h[M],e[E],ne[E],w[E],idx;
int g[N][N],key[M];
int dist[M][P];
bool st[M][P];
set<PII> edges;
int n,m,p,k,s;

void add(int a,int b,int c)
{
    e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}

void build()
{
    int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            for(int k=0;k<4;k++)
            {
                int nx = i + dx[k],ny = j + dy[k];
                if(nx<1||nx>n||ny<1||ny>m) continue;
                int a = g[i][j],b = g[nx][ny];
                if(!edges.count({a,b})) add(a,b,0);
            }
}

int bfs()
{
    memset(dist,0x3f,sizeof dist);
    queue<PII> q;
    dist[1][0]=0;
    q.push({1,key[1]});
    while(q.size())
    {
        auto t = q.front();
        q.pop();
        int ver = t.x,state = t.y;
        if(st[ver][state]) continue;
        st[ver][state]=1;
        if(ver==n*m) return dist[ver][state];
        for(int i=h[ver];~i;i=ne[i])
        {
            int j = e[i];
            if(w[i]&&!(state>>w[i]-1&1)) continue;
            int nstate=state|key[j];
            if(dist[j][nstate]>dist[ver][state]+1)
            {
                dist[j][nstate]=dist[ver][state]+1;
                q.push({j,nstate});
            }
        }
    }
    return -1;
}

int main()
{
    scanf("%d%d%d%d",&n,&m,&p,&k);
    memset(h, -1, sizeof h);
    for(int i=1,t=1;i<=n;i++)
        for(int j=1;j<=m;j++,t++)
            g[i][j]=t;
    while(k--)
    {
        int x1,y1,x2,y2,t;
        scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&t);
        int a=g[x1][y1],b=g[x2][y2];
        edges.insert({a,b}),edges.insert({b,a});
        if(t) add(a,b,t),add(b,a,t);
    }
    build();
    scanf("%d",&s);
    while(s--)
    {
        int x,y,c;
        scanf("%d%d%d",&x,&y,&c);
        int t = g[x][y];
        key[t]|=1<<c-1;
    }
    cout<<bfs()<<endl;
    return 0;
}
posted @ 2021-09-23 20:04  艾特玖  阅读(78)  评论(0)    收藏  举报