拯救大兵瑞恩
链接:链接
思路
- 状态的最短路
- 除了坐标,还需要记录手里的钥匙,钥匙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;
}
转载自原文链接, 如需删除请联系管理员。
原文链接:拯救大兵瑞恩【最短路+状态压缩】,转载请注明来源!