您的位置:首页 > 其它

CodeVs 1043 方格取数

2015-11-26 19:57 246 查看
$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
[i], result);
}

printf("%d\n", result);
return 0;
}


  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: