UVA1078 Steam Roller

Steam Roller

给定起点和终点,求网格图中的最短路,其中每条边如果在头部或尾部改变方向,那么其长度翻倍。

首先想状态,因为有翻倍这个规则,我们需要知道来到某个点时面对的方向。

但有可能在来到某个点之前不知道是否需要翻倍,但有的又已经确定并翻倍,那么有必要另用一个 0/1 状态记录。

设 \(f(i, j, dir, doubled)\) 表示:到点 \((i,j)\),来的方向为 \(dir\),来的边是否已经翻倍,到达这个状态的最短距离。

之后的难点是建图,实际上直接枚举每个状态可以到达的其它状态即可。

之后可以设一个超级源点 \(s\),将其与所有起点可以一步到达的状态连边即可。

然后是最短路的板子。

由于状态总量 \(N=r\times c\times 4\times 2\),总边数最大为 \(M=N\times 4\)。

利用 Dijkstra 的时间复杂度为 \(O(M\log N)\),完全可以通过本题。

码量较大,细节上需多加注意。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;

const int N = 110;
const int M = N * N * 10;
const int INF = 0x3f3f3f3f;
const int UP = 0, LEFT = 1, RIGHT = 2, DOWN = 3;
const int inv[] = {DOWN, RIGHT, LEFT, UP};
const int dx[] = {-1, 0, 0, 1};
const int dy[] = {0, -1, 1, 0};

int n, m, r1, c1, r2, c2, tot, G[N][N][4], Id[N][N][4][2];
int cnt, head[M], dis[M], vis[M];
struct Edge{int nxt, to, val;} ed[M * 10];
priority_queue<pair<int, int> >q;

void clear(){
	tot = cnt = 0;
	memset(G, 0, sizeof(G));
	memset(Id, 0, sizeof(Id));
	memset(head, 0, sizeof(head));
}

int read(){
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

int ID(int x, int y, int dir, int doubled){
	int& now = Id[x][y][dir][doubled];
	return now ? now : (now = ++ tot);
}

bool In(int x, int y, int dir){
	if(x < 1 || x > n || y < 1 || y > m) return false;
	return G[x][y][dir] > 0;
}

void add(int u, int v, int w){
	ed[++ cnt] = (Edge){head[u], v, w};
	head[u] = cnt;
}

void Dijkstra(){
	int s = 0;
	for(int dir = 0; dir < 4; dir ++) if(In(r1, c1, dir))
		add(s, ID(r1 + dx[dir], c1 + dy[dir], dir, 1), G[r1][c1][dir] * 2);
	memset(dis, 0x3f, sizeof(dis));
	memset(vis, false, sizeof(vis));
	q.push(make_pair(0, s)), dis[s] = 0;
	
	while(!q.empty()){
		int u = q.top().second; q.pop();
		if(vis[u]) continue;
		vis[u] = true;
		for(int i = head[u]; i; i = ed[i].nxt){
			int v = ed[i].to, w = ed[i].val;
			if(dis[v] > dis[u] + w){
				dis[v] = dis[u] + w;
				q.push(make_pair(-dis[v], v));
			}
		}
	}
}

int main(){
	int Case = 0;
	while(~scanf("%d %d %d %d %d %d", &n, &m, &r1, &c1, &r2, &c2) && n){
		clear();
		for(int i = 1; i <= n; i ++){
			for(int j = 1; j < m; j ++)
				G[i][j][RIGHT] = G[i][j + 1][LEFT] = read();
			if(i != n) for(int j = 1; j <= m; j ++)
				G[i][j][DOWN] = G[i + 1][j][UP] = read();
		}
		
		for(int i = 1; i <= n; i ++)
			for(int j = 1; j <= m; j ++)
				for(int dir = 0; dir < 4; dir ++) if(In(i, j, inv[dir]))
					for(int newdir = 0; newdir < 4; newdir ++) if(In(i, j, newdir))
						for(int doubled = 0; doubled < 2; doubled ++){
							int gi = i + dx[newdir];
							int gj = j + dy[newdir];
							int len = G[i][j][newdir], newdoubled = 0;
							if(dir != newdir){
								if(!doubled) len += G[i][j][inv[dir]];
								newdoubled = 1, len += G[i][j][newdir];
							}
							add(ID(i, j, dir, doubled), ID(gi, gj, newdir, newdoubled), len);
						}
						
		Dijkstra();
		
		int ans = INF;
		for(int dir = 0; dir < 4; dir ++) if(In(r2, c2, inv[dir]))
			for(int doubled = 0; doubled < 2; doubled ++){
				int v = dis[ID(r2, c2, dir, doubled)];
				if(!doubled) v += G[r2][c2][inv[dir]];
				ans = min(ans, v);
			}
		printf("Case %d: ", ++ Case);
		if(ans == INF) puts("Impossible"); else printf("%d\n", ans);
	}
	return 0;
}
上一篇:欧拉回路--模板


下一篇:2-SAT--UVA11294 Wedding