洛谷 P1004 方格取数
题目描述
设有N ×N的方格图(N≤9),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字0。如下图所示(见样例):
A
0 0 0 0 0 0 0 0
0 0 13 0 0 6 0 0
0 0 0 0 7 0 0 0
0 0 0 14 0 0 0 0
0 21 0 0 0 4 0 0
0 0 15 0 0 0 0 0
0 14 0 0 0 0 0 0
0 0 0 0 0 0 0 0
B
某人从图的左上角的A点出发,可以向下行走,也可以向右走,直到到达右下角的B点。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。
此人从A点到B点共走两次,试找出2条这样的路径,使得取得的数之和为最大。
输入输出格式
输入格式:
输入的第一行为一个整数N(表示N×N的方格图),接下来的每行有三个整数,前两个表示位置,第三个数为该位置上所放的数。一行单独的0表示输入结束。
输出格式:
只需输出一个整数,表示2条路径上取得的最大的和。
分析
如果只是走一遍取最大值显然可以用二维dp
走两遍dp?
这样就像贪心了,无法保证两次的和最大
因为时间对本题无影响,可以假设放出一个分身和他同时走,各走各的
那么当前最大值就是上一步最大值加上此时这人和他分身所处格子的数值
要是走重了怎么办?
相当于同一个格子取了两次,减掉一次就好了
- 因为人和分身同时走,而且只能向右或向下
所以他们走到同一个格子时步数一定是相同的qwq
下面给出代码及空间优化
一
分别记下他们的位置坐标,需要四维
#include <iostream>
#include <cstdio>
#include <cstring>
#define N 15
using namespace std;
int n;
int p[N][N];
int f[N][N][N][N];
int main(){
memset(f,0,sizeof(f));
memset(p,0,sizeof(p));
cin>>n;
int r,c,v;
do{
cin>>r>>c>>v;
p[r][c]=v;
}while( r || c || v );
for(int a=1;a<=n;++a)
{
for(int b=1;b<=n;++b)
{
for(int c=1;c<=n;++c)
{
for(int d=1;d<=n;++d)
{
f[a][b][c][d]=max(f[a][b][c][d],f[a][b-1][c][d-1]);
f[a][b][c][d]=max(f[a][b][c][d],f[a][b-1][c-1][d]);
f[a][b][c][d]=max(f[a][b][c][d],f[a-1][b][c][d-1]);
f[a][b][c][d]=max(f[a][b][c][d],f[a-1][b][c-1][d]);
//上一次的选择同右、右下、同下、下右
f[a][b][c][d]+=(p[a][b]+p[c][d]);
if(a==c && b==d)
f[a][b][c][d]-=p[a][b];//重了减掉
}
}
}
}
cout<<f[n][n][n][n]<<endl;
return 0;
}
二
设i
为当前步数,x
,y
为坐标
则x+y=i+2
- 简单证明:
在A(1,1)
时x+y=2 i=0
之后每走一步(不论向右向下)x+y
加一
步数为i
时x+y+=i
即x+y=i+2
即y=i+2-x
这样只要记步数和横坐标,共三维
#include <iostream>
#include <cstdio>
#include <cstring>
#define N 15
using namespace std;
int n;
int a[N][N];
int f[2*N][N][N];
int main(){
memset(f,0,sizeof(f));
memset(a,0,sizeof(a));
cin>>n;
int r,c,v;
do{
cin>>r>>c>>v;
a[r][c]=v;
}while( r || c || v );
f[0][1][1]=a[1][1];//不初始化会死得很惨 (1,1)上可能有值
for(int i=1;i<=2*n-2;++i)//从A到B要2*n-2步
{
for(int x1=1;x1<=i+1;++x1)//y=i+2-x>=1 -> x<=i+1
{
for(int x2=1;x2<=i+1;++x2)
{
int y1=i+2-x1,y2=i+2-x2;
f[i][x1][x2]=max(f[i][x1][x2],f[i-1][x1][x2]);
f[i][x1][x2]=max(f[i][x1][x2],f[i-1][x1-1][x2]);
f[i][x1][x2]=max(f[i][x1][x2],f[i-1][x1][x2-1]);
f[i][x1][x2]=max(f[i][x1][x2],f[i-1][x1-1][x2-1]);
f[i][x1][x2]+=(a[x1][y1]+a[x2][y2]);
if(x1==x2 && y1==y2)
f[i][x1][x2]-=a[x1][y1];
}
}
}
cout<<f[2*n-2][n][n]<<endl;
return 0;
}
三
f[i]
只与f[i-1]
有关
so存两张表就好,滚动数组实现
代码走丢了.cpp
四
其实跟01背包很像
上一维的不用存,倒序更新就好
#include <iostream>
#include <cstdio>
#include <cstring>
#define N 15
using namespace std;
int n;
int a[N][N];
int f[N][N];
int main(){
memset(f,0,sizeof(f));
memset(a,0,sizeof(a));
cin>>n;
int r,c,v;
do{
cin>>r>>c>>v;
a[r][c]=v;
}while( r || c || v );
f[1][1]=a[1][1];
for(int i=1;i<=2*n-2;++i)
{
for(int x1=i+1;x1>=1;--x1)
{
for(int x2=i+1;x2>=1;--x2)
{
int y1=i+2-x1,y2=i+2-x2;
f[x1][x2]=max(f[x1][x2],f[x1][x2]);
f[x1][x2]=max(f[x1][x2],f[x1-1][x2]);
f[x1][x2]=max(f[x1][x2],f[x1][x2-1]);
f[x1][x2]=max(f[x1][x2],f[x1-1][x2-1]);
f[x1][x2]+=(a[x1][y1]+a[x2][y2]);
if(x1==x2 && y1==y2)
f[x1][x2]-=a[x1][y1];
}
}
}
cout<<f[n][n]<<endl;
return 0;
}
本文思路借鉴: 以墨