• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
jacklee404
Never Stop!
博客园    首页    新随笔    联系   管理    订阅  订阅
1027. 方格取数 - 线性dp

题目

1027. 方格取数 - AcWing题库

思路

​ 一开始dp了两次,错误,然后又尝试第一次dp后在进行回溯,把第一次最优路径中的点更改为\(0\), 然后第二次dp,在把两次和相加,这样是错的。

​ 这两个子最优结构并不同属一个最优结构,这两个子最优结构只能保证单次最优,然后再单次最优的情况下在进行求解,这并不等于两次最优。

​ 例如如下路径

4 * 4 矩阵

0 0 2 3 0 0 0
0 0 3 0 0 0 0
0 0 3 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 4 0 0
0 0 0 0 4 0 0
0 0 2 0 4 0 0

dp的话第一次路径应该是 2->3->3->4->4->4

第二次是 3

但是最优路径是这样的 第一次:2->3->3->2->4

第二次 3->4->4

当然直观的理解,也能想到这种做法显然错误(因为我本人CPU不好使😂)

​ 我们将上述问题转化为两个人同时走,并且保证一个点的值只能取走一次的问题,可以肯定这两个问题等价:

​ 不妨设\(f[x_1][y_1][x_2][y_2]\) 表示当第两个人走到\((x_1, y_1)\), \((x_2, y_2)\)时的最优解,考虑到约束条件同时即\(x_1 + y_1 = x_2 + y_2\), 我们设\(k = x_1 + y_1 = x_2 + y_2\), 那么状态可以缩小为\(f[k][x_1][x_2]\) , 表示\((x_i, k - x_i)\).

​ 那么对于状态转移, 对于\((x_i, y_i)\)有两个状态可以转移过来, 所以总共有四个状态可以转移过来。

\((x_1 - 1, y_1), (x_2 - 1, y_2) = f[k - 1][x_1 - 1][x_2 - 1]\)

\((x_1 - 1, y_1), (x_2, y_2 - 1) = f[k - 1][x_1 - 1][x_2]\)

\((x_1, y_1 - 1), (x_2, y_2 - 1) = f[k - 1][x_1][x_2]\)

\((x_1, y_1 - 1), (x_2 - 1, y_2) = f[k - 1][x_1][x_2 - 1]\)

\(f[k][x_1][x_2] = max(f[k - 1][x_1 - 1][x_2 - 1], f[k - 1][x_1 - 1][x_2], f[k - 1][x_1][x_2], f[k - 1][x_1][x_2 - 1]) + (a[x_1][k - x_1] + a[x_2][k - x_2])\)

另外还需要判断当前的点是否存在重合,这样递推过来保证我们的子集也不存在重合的。

Code

第一次提交 WA

#include <bits/stdc++.h>
#define rep(i, j, k) for(int i = j; i <= k; i ++)
using i64 = long long;

void solve() {
	int n;
	std::cin >> n;

	std::vector<std::vector<int>> a(n + 1, std::vector<int>(n + 1));
	std::vector<std::vector<int>> dp(n + 1, std::vector<int>(n + 1));
	std::vector<std::vector<int>> dp2(n + 1, std::vector<int>(n + 1));

	int _x, _y, _val;

	while(std::cin >> _x >> _y >> _val && (_x || _y || _val)) {
		a[_x][_y] = _val;
	}

	rep(i, 1, n) {
		rep(j, 1, n) {
			dp[i][j] = std::max(dp[i - 1][j], dp[i][j - 1]) + a[i][j];
		}
	}

	int xi = n, yi = n;

	rep(i, 1, n) {
		rep(j, 1, n) {
			std::cout << a[i][j] << " \n"[j == n];
		}
	}

	puts("-------------------------------------");

	rep(i, 1, n) {
		rep(j, 1, n) {
			std::cout << dp[i][j] << " \n"[j == n];
		}
	}

	while(xi != 1 || yi != 1) {
		if(xi - 1 == 0) yi --, a[xi][yi] = 0;
		else if(yi - 1 == 0) xi --, a[xi][yi] = 0;
		else if(dp[xi - 1][yi] >= dp[xi][yi - 1]) {
			xi --;
			a[xi][yi] = 0;
		} else {
			yi --;
			a[xi][yi] = 0;
		}
	}

	puts("-------------------------------------");

	rep(i, 1, n) {
		rep(j, 1, n) {
			std::cout << a[i][j] << " \n"[j == n];
		}
	}

	// std::cout << xi << " " << yi << "\n";

	a[n][n] = 0;

	rep(i, 1, n) {
		rep(j, 1, n) {
			dp2[i][j] = std::max(dp2[i - 1][j], dp2[i][j - 1]) + a[i][j];
		}
	}

	std::cout << dp[n][n] << "\n";

	std::cout << dp[n][n] + dp2[n][n] << "\n";
}

int main() {
	int _;
	_ = 1;
	while(_ --) {
		solve();
	}
}

第二次AC:

#include <bits/stdc++.h>
#define rep(i, j, k) for(int i = j; i <= k; i ++)
using i64 = long long;

int dp[100][100][100];

int dx[]{-1,-1,0,0}, dy[]{-1,0,0,-1};

void solve() {
	int n;
	std::cin >> n;
	
	memset(dp, 0, sizeof dp);

	std::vector<std::vector<int>> a(n + 1, std::vector<int>(n + 1));

	int _x, _y, _val;

	while(std::cin >> _x >> _y >> _val && (_x || _y || _val)) {
		a[_x][_y] = _val;
	}

	auto check = [&n](int x1, int y1)->bool{
		return x1 >= 1 && x1 <= n && y1 >= 1 && y1 <= n;
	};


	rep(k, 2, 2 * n) {
		rep(x1, 1, n) {
			rep(x2, 1, n) {
				int y1 = k - x1, y2 = k - x2;
				if(x1 == x2) {
					dp[k][x1][x2] += a[x1][k - x1];
				} else {
					dp[k][x1][x2] += a[x1][k - x1] + a[x2][k - x2];
				}
				int mx = -1;
				if(check(x1, y1) && check(x2, y2)) {
					rep(i, 0, 3) {
						mx = std::max(mx, dp[k - 1][x1 + dx[i]][x2 + dy[i]]);
					}
					dp[k][x1][x2] += mx; 
				}
			}
		}
	}

	std::cout << dp[2 * n][n][n] << "\n";
}

int main() {
	int _;
	_ = 1;
	while(_ --) {
		solve();
	}
}
posted on 2023-02-01 15:45  Jack404  阅读(10)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3