题目:https://www.luogu.org/problemnew/show/P3974
题目描述
为了提高智商,ZJY开始学习组合数学。某一天她解决了这样一个问题:给一个网格图,其中某些格子有财宝。每次从左上角出发,只能往右或下走。问至少要走几次才可能把财宝全捡完。
但是她还不知足,想到了这个问题的一个变形:假设每个格子中有好多块财宝,而每一次经过一个格子至多只能捡走一块财宝,其他条件不变,至少要走几次才可能把财宝全捡完?
这次她不会做了,你能帮帮她吗?
输入输出格式
输入格式:
第一行为一个正整数t,表示数据组数
每组数据的第一行是两个正整数n和m,表示这个网格图有n行m列。
接下来n行,每行m个非负整数,表示这个格子中的财宝数量(0表示没有财宝)。
输出格式:
对于每组数据,输出一个整数,表示至少走的次数。
输入输出样例
说明
对于30%的数据,n≤5.m≤5,每个格子中的财宝数不超过5块。
对于50%的数据,n≤100,m≤100,每个格子中的财宝数不超过1000块
对于100%的数据,n≤1000,m≤1000,每个格子中的财宝不超过10^6块
解析
要算至少取几次能取完,
我们考虑有哪一些财宝是不能同一次取的。
由于原题要求是从左上到右下的,我们就从右上到坐下走,寻找一次性不能取的。
这种类型题,我们考虑dp。
设f[i][j],代表从(1,m)到(i,j)不能同一次取的有多少,
我们知道(i,j)和右上(i-1,j)是不能同时取的,
所以f[i][j]=f[i-1][j+1]+a[i][j],
我们同时考虑到(i,j)可以从(i-1,j)和(i,j+1)转移过来,所以
最终dp转移是这样的;
1 f[i][j]=f[i-1][j+1]+a[i][j]; 2 f[i][j]=max(f[i][j],f[i-1][j]); 3 f[i][j]=max(f[i][j],f[i][j+1]);
好了献上蒟蒻的代码orz:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 using namespace std; 7 const int maxn=1005; 8 int n,m,t; 9 int a[maxn][maxn]; 10 int f[maxn][maxn]; 11 int main(){ 12 scanf("%d",&t); 13 while (t--){ 14 scanf("%d%d",&n,&m); 15 for (int i=1;i<=n;++i){ 16 for (int j=1;j<=m;++j){ 17 scanf("%d",&a[i][j]); 18 } 19 } 20 for (int i=1;i<=n;++i){ 21 for (int j=m;j>=1;--j){ 22 f[i][j]=f[i-1][j+1]+a[i][j]; 23 f[i][j]=max(f[i][j],f[i-1][j]); 24 f[i][j]=max(f[i][j],f[i][j+1]); 25 } 26 } 27 printf("%d ",f[n][1]); 28 } 29 return 0; 30 }