[BZOJ4992] [Usaco2017 Feb]Why Did the Cow Cross the Road(spfa)
把每个点和曼哈顿距离距离它3步或1步的点连一条边,边权为3 * t + a[x][y]
因为,走3步,有可能是3步,也有可能是1步(其中一步拐了回来)
最后,把终点和曼哈顿距离距离它1步和2布的点连一条边,边权为 步数 * t
跑一边spfa即可
#include <queue> #include <cstdio> #include <cstring> #include <iostream> #define N 1000001 #define idx(i, j) ((i - 1) * n + j) using namespace std; int n, t, cnt; int a[101][101], d[4][N][2], tot[4]; int head[N], to[N], next[N], val[N], dis[N]; bool vis[N]; queue <int> q; inline int read() { int x = 0, f = 1; char ch = getchar(); for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1; for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0'; return x * f; } inline void add(int x, int y, int z) { to[cnt] = y; val[cnt] = z; next[cnt] = head[x]; head[x] = cnt++; } inline void spfa() { int i, u, v; memset(dis, 127, sizeof(dis)); dis[1] = 0; q.push(1); while(!q.empty()) { u = q.front(); q.pop(); vis[u] = 0; for(i = head[u]; ~i; i = next[i]) { v = to[i]; if(dis[v] > dis[u] + val[i]) { dis[v] = dis[u] + val[i]; if(!vis[v]) { q.push(v); vis[v] = 1; } } } } } int main() { int i, j, k, l, x, y; n = read(); t = read(); for(i = 1; i <= 3; i++) for(j = 0; j <= i; j++) { d[i][++tot[i]][0] = j, d[i][tot[i]][1] = i - j; d[i][++tot[i]][0] = j, d[i][tot[i]][1] = -i + j; d[i][++tot[i]][0] = -j, d[i][tot[i]][1] = i - j; d[i][++tot[i]][0] = -j, d[i][tot[i]][1] = -i + j; } memset(head, -1, sizeof(head)); for(i = 1; i <= n; i++) for(j = 1; j <= n; j++) a[i][j] = read(); for(i = 1; i <= n; i++) for(j = 1; j <= n; j++) for(l = 1; l <= 3; l += 2) for(k = 1; k <= tot[l]; k++) { x = i + d[l][k][0]; y = j + d[l][k][1]; if(1 <= x && x <= n && 1 <= y && y <= n) add(idx(i, j), idx(x, y), 3 * t + a[x][y]); } for(i = 1; i <= 2; i++) for(j = 1; j <= tot[i]; j++) { x = n + d[i][j][0]; y = n + d[i][j][1]; if(1 <= x && x <= n && 1 <= y && y <= n) add(idx(x, y), n * n, i * t); } spfa(); printf("%d\n", dis[n * n]); return 0; }