Loading

洛谷 P1004 题解

洛谷 P1004

\(N \times N\) 的方格图,我们将某些格子中放入正整数,其他的格子放 \(0\)

某人从方格图的左上角的 \(A\) 点出发,可以向右或者向下行走,直到达到右下角的 \(B\) 点。在走过的路上,他可以取走方格中的数(取走后方格中的数变成 \(0\))。

这个人总共走 \(2\) 次,请找出两条这样的路径,使得取出的数的和最大。

思路

首先,我们可以想到最暴力的暴力:搜两次。

但是,我们可以发现,因为只能往下和往右走,所以坐标有拓扑序,可以先搜一次,再做 dp。

但是,显然,我们分开做是会让时间复杂度变得很大的,所以,我们可以考虑一起做,也就是将状态变成 \((x_1, y_1, x_2, y_2)\),做 \(4\) 种转移,由于每次转移是两个点都走,所以,\(x_1 + y_1 = x_2 + y_2\) 是一直成立的,那么,只要 \(x_1 \ne x_2\),我们就可以将两个的格子的数都取走。

由于坐标有拓扑序,所以可以直接记忆化搜索和 dp,时间复杂度为 \(O(N ^ 4)\)

前面我们提到过,\(x_1 + y_1 = x_2 + y_2\),所以,我们可以枚举 \(x, y\) 的和,再枚举 \(x_1, x_2\),得到 \(y_1, y_2\),进行转移,时间复杂度为 \(O(N ^ 3)\)

代码

其实对于这道题除了搜两次以外,其他的都可以过,这里挂的 \(O(N ^ 3)\) 那种。

#include <iostream>

using namespace std;

const int N = 15;

const int dx[] = {0, 1};
const int dy[] = {1, 0};

int n, p[N][N], u, v, w, dp[2 * N][N][N];

int main() {
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n;
  while (cin >> u >> v >> w) {
    if (!u && !v && !w) {
      break;
    }
    p[u][v] = w;
  }
  int m = 2 * n;
  for (int i = 2; i <= m; i++) {
    for (int x1 = 1; x1 < i && x1 <= n; x1++) {
      for (int x2 = 1; x2 < i && x2 <= n; x2++) {
        int y1 = i - x1, y2 = i - x2, sum = 0;
        for (int j = 0; j < 2; j++) {
          for (int k = 0; k < 2; k++) {
            int a = x1 - dx[j], c = x2 - dx[k];
            sum = max(sum, dp[i - 1][a][c]);
          }
        }
        dp[i][x1][x2] = sum + p[x1][y1] + p[x2][y2] - (x1 == x2 && y1 == y2) * p[x1][y1];
      }
    }
  }
  cout << dp[m][n][n];
  return 0;
}
posted @ 2023-05-17 15:49  chengning0909  阅读(32)  评论(0编辑  收藏  举报