[TJOI2015]组合数学
题目描述
为了提高智商,ZJY开始学习组合数学。某一天她解决了这样一个问题:给一个网格图,其中某些格子有财宝。每次从左上角出发,只能往右或下走。问至少要走几次才可能把财宝全捡完。
但是她还不知足,想到了这个问题的一个变形:假设每个格子中有好多块财宝,而每一次经过一个格子至多只能捡走一块财宝,其他条件不变,至少要走几次才可能把财宝全捡完?
这次她不会做了,你能帮帮她吗?
输入输出格式
输入格式:
第一行为一个正整数t,表示数据组数
每组数据的第一行是两个正整数n和m,表示这个网格图有n行m列。
接下来n行,每行m个非负整数,表示这个格子中的财宝数量(0表示没有财宝)。
输出格式:
对于每组数据,输出一个整数,表示至少走的次数。
输入输出样例
输入样例#1
1
3 3
0 1 5
5 0 0
1 0 0
输出样例#1
10
说明
数据范围
对于30%的数据,n≤5.m≤5,每个格子中的财宝数不超过5块。
对于50%的数据,n≤100,m≤100,每个格子中的财宝数不超过1000块
对于100%的数据,n≤1000,m≤1000,每个格子中的财宝不超过10^6块
题解
不会
看了网上的题解似乎稍微明白了一点(?)
好像要用到什么\(Dilworth\)定理
\(Dilworth\)定理:\(DAG\)的最小链覆盖=最大点独立集
最小链覆盖指选出最少的链(可以重复)使得每个点都在至少一条链中
所以我还是看不懂
所以我自己感性xjb理解了一波(错了别找我)
对于这道题
独立集是两个点不能在一次走动中同时经过
所以对于一个点(i,j)
如果走了ta就不能走(i+1,j+1)或(i-1,j-1)
所以我们可以直接从左下到右上或右上到左下dp
因为这样求出来的路径一定是一次走动不能拟合的
就在f[i-1][j],f[i][j+1]中取个max,他们属于一个集合
然后再与独立集f[i-1][j+1]+val[i][j]取个max
因为走了f[i-1][j+1]就不能走(i,j)
所以是要加上val(i,j)
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
# define LL long long
const int M = 1005 ;
using namespace std ;
inline int read() {
char c = getchar() ; int x = 0 , w = 1 ;
while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
return x*w ;
}
int n , m ;
int val[M][M] ;
LL f[M][M] ;
int main() {
int T = read() ;
while(T --) {
memset(f , 0 , sizeof(f)) ;
n = read() ; m = read() ;
for(int i = 1 ; i <= n ; i ++)
for(int j = 1 ; j <= m ; j ++)
val[i][j] = read() ;
for(int i = 1 ; i <= n ; i ++)
for(int j = m ; j >= 1 ; j --)
f[i][j] = max(f[i - 1][j + 1] + val[i][j] , max(f[i - 1][j] , f[i][j + 1])) ;
printf("%lld\n",f[n][1]) ;
}
return 0 ;
}