标签:
$n \times n$ 的方格,每个格子有个自然数。一人在方格中,只能向右或下走。从左上角到右下角走两次,问最大可以取得多大的数字(每个数字取了就没了,也就是说两次经过同一个位置只能得到一个值)。
考虑两次一起走,每一行经过的段是两个线段假设是 [a, b] 和 [c, d]。当前一行状态是 [a, b] [c, d] 时转移是来自上一行的 b 等于这一行的 a, 并且上一行的 d 等于这一行的 c。
这样的话复杂度是 $O(n^9)$,大概是不能过的,但是考虑一下剪枝,可以让 $c \ge a$,因为不然就可以把两次路径后半部分交换看做是 $c \ge a$ 的。
比如 3-5 和 1-9,可以改成 1-5 和 3-9。
这样在 n = 10 的时候大概是 $3\times 10^7$。
#include <bits/stdc++.h> using std::vector; const int MAXN = 12; const int MAXS = 1 << MAXN; int dp[MAXN][MAXS]; int a[MAXN][MAXN]; int sum[MAXN][MAXN]; struct Status { int a, b, c, d; int sumValue(int i) { int res = sum[i][b + 1] - sum[i][a] + sum[i][d + 1] - sum[i][c]; if (c <= b) res -= sum[i][b + 1] - sum[i][c]; return res; } }; vector<Status> status; #define rep(i, l, r) for (int i = l; i < r; i++) int statusTable(int n) { int res = 0; rep(i0, 0, n) rep(i1, i0, n) rep(j0, i0, n) rep(j1, j0, n) { status.push_back((Status) {i0, i1, j0, j1}); res++; } return res; } int main() { int n; scanf("%d", &n); int sCnt = statusTable(n); int x, y, v; for (; scanf("%d %d %d", &x, &y, &v), (x + y + v); ) a[x][y] = v; for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) sum[i][j] = sum[i][j - 1] + a[i][j]; } rep(i, 0, sCnt) if (status[i].a == 0 && status[i].b >= status[i].c - 1) { dp[1][i] = status[i].sumValue(1); } for (int i = 2; i <= n; i++) { rep(j, 0, sCnt) rep(k, 0, sCnt) { if (status[j].b - status[k].a || status[j].d - status[k].c) continue; dp[i][k] = std::max(dp[i][k], dp[i - 1][j] + status[k].sumValue(i)); } } int result = 0; rep(i, 0, sCnt) if (status[i].d == n - 1 && status[i].b == n - 1) { result = std::max(dp[n][i], result); } printf("%d\n", result); return 0; }
标签:
原文地址:http://www.cnblogs.com/gu-castle/p/4998756.html