/************************************************************************************
描述 (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;
}