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\))。
容易发现我们计算时两次行走时重复点算了两次,加入相同判断即可。
//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) 收藏 举报