UVaLive 4128 Steam Roller (多决策最短路)

时间:2023-12-27 16:12:49

题意:给定一个图,r 根横线, c 根竖线。告诉你起点和终点,然后从起点走,每条边有权值,如果是0,就表示无法通行。走的规则是:如果你在下个路要转弯,会使这段路的时间加倍,但是如果一条路同时是这样,那么也只算两倍。起点和终点他们相连的第一条边也算两倍。问你最短时间。

析:把每个点拆成 8 个点(r, c, dir, doubled)分别下一步走哪个方向,是不是要加倍,然后每次枚举上一条,和新边,枚举上一边是不是加倍之后的,然后判断是不是要转弯,然后计算加不加倍,最后跑一次最短路,就好了。

代码如下:

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <cstdio>
#include <string>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <cstring>
#include <set>
#include <queue>
#include <algorithm>
#include <vector>
#include <map>
#include <cctype>
#include <cmath>
#include <stack>
#include <sstream>
#include <list>
#include <assert.h>
#include <bitset>
#define debug() puts("++++");
#define gcd(a, b) __gcd(a, b)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define fi first
#define se second
#define pb push_back
#define sqr(x) ((x)*(x))
#define ms(a,b) memset(a, b, sizeof a)
#define sz size()
#define pu push_up
#define pd push_down
#define cl clear()
#define all 1,n,1
#define FOR(i,x,n) for(int i = (x); i < (n); ++i)
#define freopenr freopen("in.txt", "r", stdin)
#define freopenw freopen("out.txt", "w", stdout)
using namespace std; typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> P;
const int INF = 0x3f3f3f3f;
const double inf = 1e20;
const double PI = acos(-1.0);
const double eps = 1e-8;
const int maxn = 80000 + 10;
const int maxm = 100 + 10;
const ULL mod = 10007;
const int dr[] = {-1, 0, 1, 0};
const int dc[] = {0, -1, 0, 1};
const char *de[] = {"0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"};
int n, m;
const int mon[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
const int monn[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
inline bool is_in(int r, int c) {
return r >= 0 && r < n && c >= 0 && c < m;
}
const int UP = 0, LEFT = 1, DOWN = 2, RIGHT = 3;
const int inv[] = {2, 3, 0, 1};
int id[maxm][maxm][4][2], cnt;
int g[maxm][maxm][4]; int ID(int r, int c, int dir, int doubled){
int &x = id[r][c][dir][doubled];
return x == 0 ? x = ++cnt : x;
} bool cango(int r, int c, int dir){
if(!is_in(r, c)) return false;
return g[r][c][dir] > 0;
} struct Edge{
int from, to, dist;
};
struct HeapNode{
int d, u;
bool operator < (const HeapNode &p) const{
return d > p.d;
}
}; struct Dijkstra{
int n, m;
vector<Edge> edges;
vector<int> G[maxn];
bool done[maxn];
int d[maxn]; void init(int n){
this-> n = n;
for(int i = 0; i < n; ++i) G[i].cl;
edges.cl;
} void addEdge(int from, int to, int dist){
edges.pb((Edge){from, to, dist});
m = edges.sz;
G[from].pb(m-1);
} void dijkstra(int s){
priority_queue<HeapNode> pq;
ms(d, INF); d[s] = 0;
ms(done, 0);
pq.push((HeapNode){0, s});
while(!pq.empty()){
HeapNode x = pq.top(); pq.pop();
int u = x.u;
if(done[u]) continue;
done[u] = true;
for(int i = 0; i < G[u].sz; ++i){
Edge &e = edges[G[u][i]];
if(d[e.to] > d[u] + e.dist){
d[e.to] = d[u] + e.dist;
pq.push((HeapNode){d[e.to], e.to});
}
}
}
}
};
Dijkstra dij; int readint(){ int x; scanf("%d", &x); return x; } int main(){
int r1, c1, r2, c2, kase = 0;
while(scanf("%d %d %d %d %d %d", &n, &m, &r1, &c1, &r2, &c2) == 6 && n){
--r1, --r2, --c1, --c2;
for(int r = 0; r < n; ++r){
for(int c = 0; c < m-1; ++c)
g[r][c][RIGHT] = g[r][c+1][LEFT] = readint();
if(r != n-1) for(int c = 0; c < m; ++c)
g[r][c][DOWN] = g[r+1][c][UP] = readint();
}
dij.init(n * m * 8 + 1);
cnt = 0; ms(id, 0);
for(int dir = 0; dir < 4; ++dir) if(cango(r1, c1, dir)) // the edge of source
dij.addEdge(0, ID(r1+dr[dir], c1+dc[dir], dir, 1), g[r1][c1][dir] * 2);
FOR(r, 0, n) FOR(c, 0, m) FOR(dir, 0, 4) if(cango(r, c, inv[dir]))
FOR(newdir, 0, 4) if(cango(r, c, newdir)) FOR(doubled, 0, 2){
int x = r + dr[newdir];
int y = c + dc[newdir];
int v = g[r][c][newdir], newdoubled = 0;
if(dir != newdir){
if(!doubled) v += g[r][c][inv[dir]]; // the old edge double
newdoubled = 1; v += g[r][c][newdir]; // the new edge double
}
dij.addEdge(ID(r, c, dir, doubled), ID(x, y, newdir, newdoubled), v);
}
dij.dijkstra(0);
int ans = INF;
FOR(dir, 0, 4) if(cango(r2, c2, inv[dir]))
for(int doubled = 0; doubled < 2; ++doubled){
int v = dij.d[ID(r2, c2, dir, doubled)];
if(!doubled) v += g[r2][c2][inv[dir]];
ans = min(ans, v);
}
printf("Case %d: ", ++kase);
if(ans == INF) puts("Impossible");
else printf("%d\n", ans);
}
return 0;
}