(Dilworth定理) bzoj 3977

3997: [TJOI2015]组合数学

Time Limit: 20 Sec  Memory Limit: 128 MB
Submit: 326  Solved: 219
[Submit][Status][Discuss]

Description

 给出一个网格图,其中某些格子有财宝,每次从左上角出发,只能向下或右走。问至少走多少次才能将财宝捡完。此对此问题变形,假设每个格子中有好多财宝,而每一次经过一个格子至多只能捡走一块财宝,至少走多少次才能把财宝全部捡完。

 

Input

 第一行为正整数T,代表数据组数。

每组数据第一行为正整数N,M代表网格图有N行M列,接下来N行每行M个非负整数,表示此格子中财宝数量,0代表没有

Output

 输出一个整数,表示至少要走多少次。

 

Sample Input

1
3 3
0 1 5
5 0 0
1 0 0

Sample Output

10

HINT

 

 N<=1000,M<=1000.每个格子中财宝数不超过10^6

 

对于路径上任何不相同的两个点 (x1,y1)(x2,y2),都有:

  • x1x2,y1y2
  • 若 x1>x2,则有 y1<y2;否则当 x1<x2 时, y1>y2

然后我们找到所有从右上到左下的路径,其中路径的权值和最大的那条路径的权值和就是答案了。

然后我们就可以用 Dp 解决问题了。

我们可以把每一行都翻转一下,那么就可以有:

 

Dp[i][j]=max(Dp[i1][j1]+W[i][j],Dp[i1][j],Dp[i][j1])

 

其中 W[i][j] 为这个格子的权值,那么答案就是 Dp[n][m] 了。

 

#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<cstdio>
using namespace std;
int n,dp[1005][1005],mp[1005][1005],m;
int main()
{
    int tt;
    scanf("%d",&tt);
    while(tt--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            for(int j=m;j>=1;j--)
                scanf("%d",&mp[i][j]);
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
                dp[i][j]=max(dp[i-1][j-1]+mp[i][j],max(dp[i-1][j],dp[i][j-1]));
        }
        printf("%d\n",dp[n][m]);
    }
    return 0;
}

  

posted @ 2015-05-20 09:37  waterfull  阅读(221)  评论(0编辑  收藏  举报