P1004 [NOIP2000 提高组] 方格取数

容易发现本题要求最长带权路径,遂想到 dp。

这类问题的状态很好设计,用 \(f_{i, j}\) 表示走到格子 \(a_{i, j}\) 时的最长带权路径。

由于本题需要求两次,所以增加第二次的两维,合并为 \(f_{i, j, k, l}\),代表第一次走到格子 \(a_{i, j}\),第二次走到格子 \(a_{k, l}\) 的答案。

状态设计已经解决,可以思考转移。

转移方程较为简单:

\[f_{i, j, k, l} = \max({f_{i - 1, j, k - 1, l}, f_{i - 1, j, k, l - 1}, f_{i, j - 1, k - 1, l}, f_{i, j - 1, k, l - 1}}) + a_{i, j} + a_{k, l} \]

这个公式有点头晕,略微口语来讲就是从两次的上方或左方继承,加上格子的权值,如果还是不懂先去学前置知识

好的,现在让我们来写代码。

//WA 0 pts

#include<bits/stdc++.h>
using namespace std;
int n, a[15][15], f[15][15][15][15];
int main(){
	cin >> n;
	while(1){
		int x, y, v; cin >> x;
		if(x == 0) break;
		cin >> y >> v;
		a[x][y] = v;
	}
	for(int i = 1; i <= n; i ++)
		for(int j = 1; j <= n; j ++)
			for(int k = 1; k <= n; k ++)
				for(int l = 1; l <= n; l ++){
					f[i][j][k][l] = max({f[i - 1][j][k - 1][l], f[i - 1][j][k][l - 1], f[i][j - 1][k - 1][l], f[i][j - 1][k][l - 1]}) + a[i][j] + a[k][l];
					//状态转移方程
				}
	cout << f[n][n][n][n];
	return 0;
}

于是我们得到了 0 分的好成绩

原因其实很简单,重新读题。

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

容易发现我们计算时两次行走时重复点算了两次,加入相同判断即可。

Accepted

//AC 100 pts

#include<bits/stdc++.h>
using namespace std;
int n, a[15][15], f[15][15][15][15];
int main(){
	cin >> n;
	while(1){
		int x, y, v; cin >> x;
		if(x == 0) break;
		cin >> y >> v;
		a[x][y] = v;
	}
	for(int i = 1; i <= n; i ++)
		for(int j = 1; j <= n; j ++)
			for(int k = 1; k <= n; k ++)
				for(int l = 1; l <= n; l ++){
					f[i][j][k][l] = max({f[i - 1][j][k - 1][l], f[i - 1][j][k][l - 1], f[i][j - 1][k - 1][l], f[i][j - 1][k][l - 1]}) + a[i][j] + a[k][l];
					//状态转移方程
					if(i == k && j == l) f[i][j][k][l] -= a[i][j];//重复计算减去一次
				}
	cout << f[n][n][n][n];
	return 0;
}

posted on 2025-01-30 22:39  zhangzirui66  阅读(62)  评论(0)    收藏  举报

导航