拯救大兵瑞恩【最短路+状态压缩】

拯救大兵瑞恩

链接:链接

思路

  • 状态的最短路
  • 除了坐标,还需要记录手里的钥匙,钥匙10种可以状态压缩记录
  • 拿钥匙不花费,我们过一个点拿一个钥匙,这是拿钥匙状态发生转移,花费为0,
  • 其他走法,状态发生改变,花费为1
  • 可以用双端队列代替堆优化distara

代码注意

  • 为了简化代码,我们把二维变为一维坐标
  • g[i][j]=idx;
  • 建图加边的时候,用set存下来有门的边,然后for循环连出可以到达的边
  • 存钥匙,用数组开纪录钥匙二进制,注意:一个点可以有多个钥匙
  • 双端队列,0加在队首,1加在队尾
#include<iostream>
#include<string.h>
#include<set>
#include<deque>
#include<algorithm>
using namespace std;
const int N=10000;
typedef pair<int,int>pll;
set<pll>edges;
int h[N],ne[N],e[N],w[N],idx;
int n,m,k,P;
int g[N][N];
int keys[N];
int dis[111][1<<10];
bool st[111][1<<10];
void add(int a,int b,int c)
{
    w[idx]=c;
    e[idx]=b;
    ne[idx]=h[a];
    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 a=g[i][j];
                int x=i+dx[k],y=j+dy[k];
                if(x<1||x>n||y<1||y>m) continue;
                int b=g[x][y];
                if(edges.count(make_pair(a,b))) continue;
                add(a,b,0);
                add(b,a,0);
            }
        }
}
int bfs()
{
    memset(dis,0x3f,sizeof dis);
    dis[1][0]=0;
    deque<pll>q;
    q.push_back(make_pair(1,0));
    while(q.size())
    {
        pll t=q.front();
        q.pop_front();
        int ver=t.first;
        int statea=t.second;
        if(st[ver][statea]) continue;
        st[ver][statea]=1;
        if(ver==n*m) return dis[ver][statea];
        if(keys[ver])
        {
            int stateb=statea | keys[ver];
            if(dis[ver][stateb]>dis[ver][statea])
            {
                dis[ver][stateb]=dis[ver][statea];
                q.push_front(make_pair(ver,stateb));
            }
        }
        for(int i=h[ver];~i;i=ne[i])
        {
            int j=e[i];
            if(w[i] && !(statea>>(w[i]-1)&1)) continue;
            if(dis[j][statea]>dis[ver][statea]+1)
            {
                dis[j][statea]=dis[ver][statea]+1;
                q.push_back(make_pair(j,statea));
            }
        }
    }
    return -1;
}
int main()
{
    int k;
    memset(h,-1,sizeof h);
    cin>>n>>m>>P>>k;
    for(int i=1,t=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            g[i][j]=t++;

    while(k--)
    {
        int x1,x2,y1,y2,c;
        cin>>x1>>y1>>x2>>y2>>c;
        int a=g[x1][y1],b=g[x2][y2];
        if(c) add(a,b,c),add(b,a,c);
        edges.insert(make_pair(a,b));
        edges.insert(make_pair(b,a));
    }
    int s;
    cin>>s;
    while(s--)
    {
        int x,y,c;
        cin>>x>>y>>c;
        int id=g[x][y];
        keys[id]|=1<<(c-1);
    }
    build();
    cout<<bfs()<<endl;
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值