网络流24题(十四)
网络流24题(十四)
十四、孤岛营救问题
题目描述
1944 年,特种兵麦克接到国防部的命令,要求立即赶赴太平洋上的一个孤岛,营救被敌军俘虏的大兵瑞恩。瑞恩被关押在一个迷宫里,迷宫地形复杂,但幸好麦克得到了迷宫的地形图。迷宫的外形是一个长方形,其南北方向被划分为 N 行,东西方向被划分为 M 列,于是整个迷宫被划分为 N×M 个单元。每一个单元的位置可用一个有序数对(单元的行号,单元的列号)来表示。南北或东西方向相邻的 2 个单元之间可能互通,也可能有一扇锁着的门,或者是一堵不可逾越的墙。迷宫中有一些单元存放着钥匙,并且所有的门被分成P 类,打开同一类的门的钥匙相同,不同类门的钥匙不同。
大兵瑞恩被关押在迷宫的东南角,即 (N,M) 单元里,并已经昏迷。迷宫只有一个入口,在西北角。也就是说,麦克可以直接进入 (1,1) 单元。另外,麦克从一个单元移动到另一个相邻单元的时间为 1,拿取所在单元的钥匙的时间以及用钥匙开门的时间可忽略不计。
试设计一个算法,帮助麦克以最快的方式到达瑞恩所在单元,营救大兵瑞恩。
输入格式
第 1 行有 3 个整数,分别表示 N,M,P 的值。
第 2 行是 1 个整数 K,表示迷宫中门和墙的总数。
第 \(I+2\) 行\((1≤I≤K)\),有 5 个整数,依次为\(X_{i1},Y_{i1},X_{i2},Y_{i2},G_i\):
-
当 \(G_i \geq 1\) 时,表示 \((X_{i1},Y_{i1})\) 单元与 \((X_{i2},Y_{i2})\) 单元之间有一扇第 \(G_i\) 类的门
-
当 \(G_i=0\) 时,表示 \((X_{i1},Y_{i1})\) 单元与 \((X_{i2},Y_{i2})\) 单元之间有一堵不可逾越的墙(其中,\(|X_{i1}-X_{i2}|+|Y_{i1}-Y_{i2}|=1,0\leq G_i\leq P\))。
第 \(K+3\) 行是一个整数 \(S\),表示迷宫中存放的钥匙总数。
第 \(K+3+J\) 行\((1\leq J\leq S)\),有 3 个整数,依次为 \(X_{i1},Y_{i1},Q_i\):表示第 \(J\) 把钥匙存放在 \((X_{i1},Y_{i1})\)单元里,并且第 \(J\) 把钥匙是用来开启第 \(Q_i\) 类门的。(其中\(1\leq Q_i\leq\))。
输入数据中同一行各相邻整数之间用一个空格分隔。
输出格式
将麦克营救到大兵瑞恩的最短时间的值输出。如果问题无解,则输出 -1。
题解
模型:
状压分层图最短路
有没有某把钥匙是两种状态,考虑二进制状压表示钥匙的有无,根据状态的变化还要建立分层图,分层图之间的边长为0。
因为边权为1或者0所以最后求最短路使用bfs即可。
建图与实现:
将坐标\((i,j)\)压缩为一个正整数\(x\)
\(G[x_1][x_2] = q\)表示从\(x_1\)到\(x_2\)需要第\(q\)把钥匙通行,-1表示任意通行,0不通行。
\(f[i][x]\)表示到第\(i\)层\(x\)坐标时的最短路。
\(key[x]\)表示\(x\)位置的钥匙。(这个傻逼题目一个位置有多把钥匙,一个钥匙可以无穷用)
然后\(bfs\)跑就行。
代码
ll n,m,p,num;
const ll N = (1<<14)+5;
ll G[12*12][12*12];//需要钥匙
bool vis[N][12*12] = {false};
ll f[N][12*12];
vector<ll> key[12*12];
#define pr pair<ll,ll>//层数,位置
ll to[5][5] = {{0,1},{0,-1},{-1,0},{1,0}};
void bfs(){
memset(f,0x3f,sizeof f);
queue<pr> q;
q.push({0,1});
vis[0][1] = true;
f[0][1] = 0;
while(!q.empty()){
pr u = q.front();
q.pop();
//cout<<u.first<<' '<<u.second<<' '<<key[u.second]<<endl;
for(auto k:key[u.second]) {
if (!(u.first >> (k - 1) & 1)) {
ll t = u.first + (1 << (k - 1));
//cout<<t<<endl;
if (!vis[t][u.second]) {
q.push({t, u.second});
f[t][u.second] = f[u.first][u.second];
vis[t][u.second] = true;
}
}
}
ll x1 = (u.second-1)/m+1,y1 = (u.second-1)%m+1;
for(ll i = 0;i < 4;i++){
ll x2 = x1+to[i][0],y2 = y1+to[i][1];
if(x2<=n&&x2>=1&&y2<=m&&y2>=1){
ll jg = G[(x1-1)*m+y1][(x2-1)*m+y2];
if(jg == 0)continue;
if(((u.first>>(jg-1))&1) || jg == -1) {
if(!vis[u.first][(x2 - 1) * m + y2]) {
f[u.first][(x2 - 1) * m + y2] = f[u.first][u.second]+1;
q.push({u.first, (x2 - 1) * m + y2});
vis[u.first][(x2 - 1) * m + y2] = true;
}
}
}
}
}
ll ans = 0x3f3f3f3f;
for(ll i = 0;i <= (1<<p)-1;i++){
ans = min(ans,f[i][n*m]);
}
if(ans == 0x3f3f3f3f)ans = -1;
cout<<ans<<endl;
}
int main() {
cin>>n>>m>>p;
ll k;cin>>k;
memset(G,-1,sizeof G);
for(ll i = 1;i <= k;i++){
ll x1,y1,x2,y2,g;cin>>x1>>y1>>x2>>y2>>g;
ll u = (x1-1)*m+y1,v = (x2-1)*m+y2;
//cout<<u<<' '<<v<<endl;
G[u][v] = G[v][u] = g;
}
cin>>num;
memset(key,0,sizeof key);
for(ll i = 1;i <= num;i++){
ll x,y,o;cin>>x>>y>>o;
key[(x-1)*m+y].push_back(o);
}
bfs();
return 0;
}