版权声明:神犇使用记得标明出处哦QAQ https://blog.csdn.net/qq_41717018/article/details/83386792
组合数学(math)
时间限制: 2 Sec 内存限制: 128 MB
题目描述
为了提高智商,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块。
题解:
可以看看这篇博客:https://www.cnblogs.com/Konjakmoyu/p/6680997.html
我大概yy了一下:
最开始,想到可能可以有个网络流,跑一遍类似有流量下界的最小流的东西。
然后看看数据范围。。。
假设有两个点无法互相依照题中方法相互到达。
那么,对于答案来说,至少是有两条路径要通过这两个点才会走完。
于是,考虑有一条链,链上两个点两两互相不可到达,并且路径都是经过这条链上的每个点。
这条链就叫做反链。
有个Dilworth定理:最少链数=最大权值反链的权值
这题相当于就是用最小的最大值为1的链,用最小条数覆盖这张图。、
然后就DP出最长反链即可。
证明挖坑待补。
#include<bits/stdc++.h>
using namespace std;
#define in inline
#define re register
#define rep(i,a,b) for(re int i=a;i<=b;i++)
#define repd(i,a,b) for(re int i=a;i>=b;i--)
#define For(i,a,b) for(int i=a;i<b;i++)
#define _(d) while(d(isdigit(ch=getchar())))
template<class T>in void g(T&t){T x,f=1;char ch;_(!)ch=='-'?f=-1:f;x=ch-48;_()x=x*10+ch-48;t=f*x;}
const int N=1004;
int a[N][N],f[N][N],n,m;
int main(){
//freopen(".in","r",stdin);freopen(".out","w",stdout);
int T;g(T);
while(T--){
g(n),g(m);
rep(i,1,n) rep(j,1,m) g(f[i][j]);
rep(j,1,m){
repd(i,n,1){
f[i][j]=max(f[i][j]+f[i+1][j-1],max(f[i+1][j],f[i][j-1]))+a[i][j];
}
}
cout<<f[1][n]<<endl;
}
return 0;
}