[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 ;
}
posted @ 2018-10-15 11:43  beretty  阅读(302)  评论(0编辑  收藏  举报