题目描述
1944 年,特种兵麦克接到国防部的命令,要求立即赶赴太平洋上的一个孤岛,营救被敌军俘虏的大兵瑞恩。瑞恩被关押在一个迷宫里,迷宫地形复杂,但幸好麦克得到了迷宫的地形图。迷宫的外形是一个长方形,其南北方向被划分为N 行,东西方向被划分为M列,于是整个迷宫被划分为 N×M 个单元。每一个单元的位置可用一个有序数对(单元的行号,单元的列号)来表示。南北或东西方向相邻的 2 个单元之间可能互通,也可能有一扇锁着的门,或者是一堵不可逾越的墙。迷宫中有一些单元存放着钥匙,并且所有的门被分成 P类,打开同一类的门的钥匙相同,不同类门的钥匙不同。
大兵瑞恩被关押在迷宫的东南角,即(N,M)单元里,并已经昏迷。迷宫只有一个入口,在西北角。也就是说,麦克可以直接进入(1,1)单元。另外,麦克从一个单元移动到另一个相邻单元的时间为1,拿取所在单元的钥匙的时间以及用钥匙开门的时间可忽略不计。
试设计一个算法,帮助麦克以最快的方式到达瑞恩所在单元,营救大兵瑞恩。
输入格式
第一行是三个整数,依次表示N,M,P的值;(3≤N,M≤15,1≤P≤10)
第二行是一个整数K,表示迷宫中门和墙的总个数;
第i+2行(1≤i≤K),有5个整数,依次为Xi1,Yi1,Xi2,Yi2,Gi:
当Gi≥1 时,表示(Xi1,Yi1)单元与(Xi2,Yi2) 单元之间有一扇第Gi类的门,当Gi=0时, 表示(Xi1,Yi1)单元与(Xi2,Yi2) 单元之间有一堵不可逾越的墙;
(其中,|Xi1-Xi2|+|Yi1-Yi2|=1,0≤Gi≤P)
第K+3 行是一个整数S,表示迷宫中存放的钥匙总数;
第K+3+j行(1≤j≤S),有3个整数,依次为Xi1,Yi1,Qi:表示第j把钥匙存放在(Xi1,Yi1) 单元里,并且第j把钥匙是用来开启第Qi类门的。(其中1≤Qi≤P)
注意:输入数据中同一行各相邻整数之间用一个空格分隔。
输出格式
输出一个整数T,表示麦克营救到大兵瑞恩的最短时间的值,若不存在可行的营救方案则输出-1。
样例数据
样例输入
4 4 9
9
1 2 1 3 2
1 2 2 2 0
2 1 2 2 0
2 1 3 1 0
2 3 3 3 0
2 4 3 4 1
3 2 3 3 0
3 3 4 3 0
4 3 4 4 0
2
2 1 2
4 2 1
样例输出
14
题目分析
分层图,调试爆炸。。。
样例数据太大了这里给一份自己造的样例:
3 3 2
5
1 2 2 2 0
1 3 2 3 0
1 1 2 1 1
2 1 2 2 2
3 1 3 2 0
2
1 3 1
3 1 2
图如下:
枚举拥有钥匙的状态,并以此分层,故有2^p层。
如上图,将图分为
第0层:没有钥匙
第1层:拥有钥匙1
第2层:拥有钥匙2
第3层:拥有钥匙1,2
若每一层有未拥有的钥匙,就向它所代表的层的钥匙处建一条边,边权为0
每层图内建立边,边权为1,其中没有钥匙的门和墙不能走。
然后调上1个小时就出来了233
源代码
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
inline const int Get_Int() {
int num=0,bj=1;
char x=getchar();
while(x<'0'||x>'9') {
if(x=='-')bj=-1;
x=getchar();
}
while(x>='0'&&x<='9') {
num=num*10+x-'0';
x=getchar();
}
return num*bj;
}
const int maxn=500005;
struct Edge {
int from,to,dist;
};
struct Spfa {
int n,m;
vector<Edge>edges;
vector<int>G[maxn];
bool inque[maxn];
int dist[maxn],used[maxn],path[maxn];
void init(int n) {
this->n=n;
edges.clear();
for(int i=0; i<=n; i++)G[i].clear();
}
void AddEdge(int from,int to,int dist) {
edges.push_back((Edge) {
from,to,dist
});
m=edges.size();
G[from].push_back(m-1);
}
bool main(int s) {
for(int i=0; i<=n; i++)dist[i]=0x7fffffff/2;
memset(inque,0,sizeof(inque));
memset(used,0,sizeof(used));
deque<int>Q;
Q.push_back(s);
dist[s]=0;
path[s]=s;
inque[s]=1;
used[s]++;
while(!Q.empty()) {
int Now=Q.front();
Q.pop_front();
inque[Now]=0;
for(int i=0; i<G[Now].size(); i++) {
Edge& e=edges[G[Now][i]];
int Next=e.to;
if(dist[Next]>dist[Now]+e.dist) {
dist[Next]=dist[Now]+e.dist;
path[Next]=Now;
if(!inque[Next]) {
used[Next]++;
if(used[Next]==edges.size())return false; //负权回环
if(!Q.empty()&&dist[Next]<dist[Q.front()])Q.push_front(Next); //SLF优化
else Q.push_back(Next);
inque[Next]=1;
}
}
}
}
return true;
}
};
const int Dirx[]= {0,1,0},Diry[]= {0,0,1};
int n,Floor,Floorn,Row,Line,DoorKind,Door,Keynum,KeynumofKind[2005],map[505][505],ans=0x7fffffff/2;
struct Point {
int x,y;
} Key[2005][2005];
int GetPos(int x,int y) { //二维坐标
return (x-1)*Line+y;
}
int GetPos(int FloorNum,int x,int y) { //三维坐标
return Floor*FloorNum+GetPos(x,y);
}
Spfa spfa;
void Build_Graph() {
bool HaveKey[2005];
Floor=Row*Line;
Floorn=(1<<DoorKind);
n=Floor*Floorn;
spfa.init(n);
for(int Floornum=0; Floornum<Floorn; Floornum++) { //层(同时压缩有2进制所拥有钥匙)
for(int i=1; i<=Row; i++)
for(int j=1; j<=Line; j++) { //建立同层图
int Now=GetPos(i,j);
for(int k=1; k<=2; k++) {
int Nextx=i+Dirx[k],Nexty=j+Diry[k],Next=GetPos(Nextx,Nexty);
if(Nextx<1||Nextx>Row||Nexty<1||Nexty>Line||map[Now][Next]==-1)continue;
if(map[Now][Next]==0||(Floornum&1<<(map[Now][Next]-1))) {
spfa.AddEdge(GetPos(Floornum,i,j),GetPos(Floornum,Nextx,Nexty),1);
spfa.AddEdge(GetPos(Floornum,Nextx,Nexty),GetPos(Floornum,i,j),1);
}
}
}
for(int i=1; i<=Keynum; i++) { //建立跨维边
if((Floornum&1<<(i-1))==0) {
int Status=Floornum^(1<<(i-1));
for(int j=1; j<=KeynumofKind[i]; j++) {
int Now=GetPos(Floornum,Key[i][j].x,Key[i][j].y),Next=GetPos(Status,Key[i][j].x,Key[i][j].y);
spfa.AddEdge(Now,Next,0);
}
}
}
}
}
int main() {
Row=Get_Int();
Line=Get_Int();
DoorKind=Get_Int();
Door=Get_Int();
for(int i=1; i<=Door; i++) {
int x1=Get_Int(),y1=Get_Int(),x2=Get_Int(),y2=Get_Int(),Kind=Get_Int();
if(Kind==0)Kind=-1;
map[GetPos(x1,y1)][GetPos(x2,y2)]=map[GetPos(x2,y2)][GetPos(x1,y1)]=Kind;
}
Keynum=Get_Int();
for(int i=1; i<=Keynum; i++) {
int x=Get_Int(),y=Get_Int();
int Kind=Get_Int();
KeynumofKind[Kind]++;
Key[Kind][KeynumofKind[Kind]].x=x;
Key[Kind][KeynumofKind[Kind]].y=y;
}
Build_Graph();
int Start=GetPos(0,1,1);
spfa.main(Start);
for(int i=0; i<Floorn; i++)ans=min(ans,spfa.dist[GetPos(i,Row,Line)]);
if(ans==0x7fffffff/2)puts("-1");
else printf("%d\n",ans);
return 0;
}