动态规划、最小费用流例题 — nyoj_712 探寻宝藏
/************************************************************************************ 描述 (http://acm.nyist.net/JudgeOnline/problem.php?pid=712 探 寻 宝 藏) 传说HMH大沙漠中有一个M*N迷宫,里面藏有许多宝物。某天,Dr.Kong找到了迷宫的地图, 他发现迷宫内处处有宝物,最珍贵的宝物就藏在右下角,迷宫的进出口在左上角。当然,迷宫 中的通路不是平坦的,到处都是陷阱。Dr.Kong决定让他的机器人卡多去探险。但机器人卡多从 左上角走到右下角时,只会向下走或者向右走。从右下角往回走到左上角时,只会向上走或者 向左走,而且卡多不走回头路。(即:一个点最多经过一次)。当然卡多顺手也拿走沿路的每 个宝物。Dr.Kong希望他的机器人卡多尽量多地带出宝物。请你编写程序,帮助Dr.Kong计算一 下,卡多最多能带出多少宝物。 输入 第一行: K 表示有多少组测试数据。 接下来对每组测试数据: 第1行: M N 第2~M+1行: Ai1 Ai2 ……AiN (i=1,…..,m) 【约束条件】 2≤k≤5 1≤M, N≤50 0≤Aij≤100 (i=1,….,M; j=1,…,N) 所有数据都是整数。 数据之间有一个空格。 输出 对于每组测试数据,输出一行:机器人卡多携带出最多价值的宝物数 样例输入 2 2 3 0 10 10 10 10 80 3 3 0 3 9 2 8 5 5 7 100 样例输出 120 134 **************************************************************************************/ /************************************************************************************* 方法一:DP 双线dp - 即同时考虑两条不相交的线,使其线上的和最大 显然我们需要记录每一步时两个线同时往前走的位置(这样可以容易的控制其不相交). 状态:dp[k,(x1,y1),(x2,y2)] 在第k步,双线里一线在(x1,y1) 二线在(x2,y2) 的最大和 转移:dp[k,(x1,y1),(x2,y2)] = max(dp[k-1,(x1-1,y1),(x2-1,y2)], dp[k-1,(x1,y1-1),(x2-1,y2)], dp[k-1,(x1-1,y1),(x2,y2-1)], dp[k-1,(x1,y1-1),(x2,y2-1)]) + A[x1][y1] + A[x2][y2]; ***************************************************************************************/ #include <cstdio> #include <algorithm> using namespace std; int dp[110][55][55], A[55][55]; int K, M, N; bool ok(int s, int x1, int x2) { if (x1 == x2) return false; if (s-x1 < 1 || s-x1 > N || s-x2 < 1 || s-x2 > N) return false; return true; } int main() { scanf("%d", &K); while (K--) { scanf("%d%d", &M, &N); for (int i = 1; i <= M; ++i) for (int j = 1; j <= N; ++j) scanf("%d", &A[i][j]); int S = M+N; dp[2][1][1] = A[1][1]; for (int s = 3; s <= S; ++s) for (int x1 = 1; x1 <= M; ++x1) for (int x2 = 1; x2 <= M; ++x2) { if (!ok(s, x1, x2)) continue; int _max = max(dp[s-1][x1-1][x2-1], dp[s-1][x1-1][x2]); int _max2 = max(dp[s-1][x1][x2-1], dp[s-1][x1][x2]); dp[s][x1][x2] = max(_max, _max2)+A[x1][s-x1]+A[x2][s-x2]; } int ret = max(dp[S-1][M-1][M], dp[S-1][M][M-1])+A[M][N]; printf("%d\n", ret); } return 0; } //方法二:最小费用流 #include <cstdio> #include <cstring> #include <queue> #include <algorithm> using namespace std; const int INF = 0x3f3f3f3f; const int maxv = 6000; const int maxe = 20010; int src, dest, nnode, nedge; int point[maxe], capa[maxe], cost[maxe], next[maxe]; int head[maxv], q[maxv], d[maxv], p[maxv], num[maxv]; bool inq[maxv]; void init(int n, int s, int t) { nnode = n; nedge = 0; src = s; dest = t; memset(head, -1, nnode*sizeof(head[0])); } void addEdge(int u, int v, int c, int w) { point[nedge] = v; capa[nedge] = c; cost[nedge] = w; next[nedge] = head[u]; head[u] = nedge++; point[nedge] = u; capa[nedge] = 0; cost[nedge] = -w; next[nedge] = head[v]; head[v] = nedge++; } bool spfa(void) { int front = 0, rear = 1; memset(inq, false, sizeof(inq)); memset(d, 0x3f, sizeof(d)); d[src] = 0; q[front] = src; inq[src] = true; p[src] = -1; while (front != rear) { int u = q[front++]; inq[u] = false; for (int i = head[u]; i != -1; i = next[i]) { int v = point[i]; if (capa[i] > 0 && d[v] > d[u]+cost[i]) { d[v] = d[u] + cost[i]; p[v] = i; if (!inq[v]) { if (front > 0 && d[v] <= q[front]) q[--front] = v; else q[rear++] = v; inq[v] = true; } } } } return d[dest] != INF; } void solve() { int ret = 0, delta = INF; spfa(); for (int i = p[dest]; i != -1 ; i = p[point[i^1]]) delta = min(capa[i], delta); for (int i = p[dest]; i != -1 ; i = p[point[i^1]]) { capa[i] -= delta; capa[i^1] += delta; } ret += d[dest]; spfa(); ret += d[dest]; printf("%d\n", -ret); } int A[110][110]; int main() { int K, M, N; scanf("%d\n", &K); while (K--) { scanf("%d%d", &M, &N); init(M*N*2, 0, M*N*2-1); for (int i = 0; i < M; ++i) for (int j = 0; j < N; ++j) scanf("%d", &A[i][j]); for (int i = 0; i < M; ++i) for (int j = 0; j < N; ++j) { addEdge(i*N+j, i*N+j+M*N, 1, -A[i][j]); if (i+1 < M) addEdge(i*N+j+M*N, (i+1)*N+j, 1, 0); if (j+1 < N) addEdge(i*N+j+M*N, i*N+j+1, 1, 0); } addEdge(src, src+M*N, 1, 0); addEdge(dest-M*N, dest, 1, 0); solve(); } return 0; }