4777: 方格取数
Time Limit(Common/Java):1000MS/3000MS Memory Limit:65536KByteTotal Submit: 11 Accepted:10
Description
设有N*N的方格图(N<=10,我们将其中的某些方格中填入正整数,而其他的方格中则放入数字0。如下图所示(见样例):
某人从图的左上角的A 点出发,可以向下行走,也可以向右走,直到到达右下角的B点。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。
此人从A点到B 点共走两次,试找出2条这样的路径,使得取得的数之和为最大。
Input
输入的第一行为一个整数N(表示N*N的方格图),接下来的每行有三个整数,前两个表示位置,第三个数为该位置上所放的数。一行单独的0表示输入结束。
Output
只需输出一个整数,表示2条路径上取得的最大的和。
Sample Input
8
2 3 13
2 6 6
3 5 7
4 4 14
5 2 21
5 6 4
6 3 15
7 2 14
0 0 0
Sample Output
67
我觉得大家第一反应应该都是做一次DP之后再做一次,但是这是在贪心,这次走完最大值的路径下次就不走了
所以其实要一起考虑,算法复杂度是n^3,它是有四个点的,包括前一次经过的点,而且步数的几个点是确定的,在一条斜线上
#include <bits/stdc++.h> using namespace std; #define max4(a, b, c, d) max(max(a, b), max(c, d)) int dp[21][11][11]; int a[11][11]; int main() { int n, x, y, z; scanf("%d", &n); while (~scanf("%d%d%d", &x, &y, &z) && (x || y || z))a[x][y] = z; for (int k = 1; k < n * 2; k++) for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) { if (i > k || j > k || k - j + 1 > n || k - i + 1 > n)continue; dp[k][i][j] = max4(dp[k - 1][i][j], dp[k - 1][i - 1][j], dp[k - 1][i][j - 1], dp[k - 1][i - 1][j - 1]); dp[k][i][j] += a[k - j + 1][j]; if (i != j)dp[k][i][j] += a[k - i + 1][i]; } cout << dp[n * 2 - 1][n][n]; return 0; }
升级版的k步,需要使用最大流求解
首先拆点,将一个点拆成x和y,然后从x到y连一条容量为1,流量为x的边
然后再连一条容量为inf,费用为0的边
这样即可保证一个点可以走多次,而数只能取一次。然后连接a和b时,从a的y向b的x连一条容量为inf,费用为0的边。最后跑最大费用最大流即可。
#include <bits/stdc++.h> using namespace std; const int N = 1e4 + 5; const int M = 1e5 + 5; const int INF = 0x3f3f3f3f; int FIR[N], TO[M], CAP[M], FLOW[M], COST[M], NEXT[M], tote; int pre[N], dist[N], q[400000]; bool vis[N]; int n, k, S, T; void init() { tote = 0; memset(FIR, -1, sizeof(FIR)); } void add(int u, int v, int cap, int cost) { TO[tote] = v; CAP[tote] = cap; FLOW[tote] = 0; COST[tote] = cost; NEXT[tote] = FIR[u]; FIR[u] = tote++; TO[tote] = u; CAP[tote] = 0; FLOW[tote] = 0; COST[tote] = -cost; NEXT[tote] = FIR[v]; FIR[v] = tote++; } bool SPFA(int s, int t) { memset(dist, INF, sizeof(dist)); memset(vis, false, sizeof(vis)); memset(pre, -1, sizeof(pre)); dist[s] = 0; vis[s] = true; q[1] = s; int head = 0, tail = 1; while (head != tail) { int u = q[++head]; vis[u] = false; for (int v = FIR[u]; v != -1; v = NEXT[v]) { if (dist[TO[v]] > dist[u] + COST[v] && CAP[v] > FLOW[v]) { dist[TO[v]] = dist[u] + COST[v]; pre[TO[v]] = v; if (!vis[TO[v]]) { vis[TO[v]] = true; q[++tail] = TO[v]; } } } } return pre[t] != -1; } void MCMF(int s, int t, int &cost, int &flow) { flow = cost = 0; while (SPFA(s, t)) { int Min = INF; for (int v = pre[t]; v != -1; v = pre[TO[v ^ 1]]) Min = min(Min, CAP[v] - FLOW[v]); for (int v = pre[t]; v != -1; v = pre[TO[v ^ 1]]) { FLOW[v] += Min; FLOW[v ^ 1] -= Min; cost += COST[v] * Min; } flow += Min; } } int main() { int ca; cin>>ca; while(ca--) { init(); cin >> n >> k; S = 0; T = n * n * 2 + 1; int ff=n*n; for (int i = 1; i <= n; i++) for (int j = 1, x; j <= n; j++) { cin>>x; int a=(i-1)*n+j,b=a+1,c=a+n; add(a,ff+a,INF,0),add(a,ff+a,1,-x); if(j<n)add(ff+a,b,INF,0); if(i<n)add(ff+a,c,INF,0); } add(S, 1, k, 0); add(T - 1, T, k, 0); int cost, flow; MCMF(S, T, cost, flow); printf("%d\n", -cost); } return 0; }