组合数学

题目描述

为了提高智商,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块

 

 

 

解析:

首先明确一个定理:最小链覆盖=最长反链(即在这条链上任意两点都无法互相连通)

而最小链覆盖即为把每个需要覆盖的点都覆盖一遍,最少需要多少条链,也就是我们求的东西。

而将求最小链覆盖转为最长反链时,我们就将从左上到右下转化为从右上到左下,便可以dp了。

dp[i][j]表示从(1,m)到(i,j)不能同一次取的有多少

因为是最长反链

所以(i,j)和右上(i-1,j+1)是不能同时取的

所以dp[i][j]=dp[i-1][j+1]+dp[i][j]

(i,j)可以从(i-1,j)和(i,j+1)转移过来,所以

最终dp转移是这样的;

 

dp[i][j]=max(max(dp[i-1][j+1]+a[i][j],dp[i-1][j]),dp[i][j+1])

 

所以,该上代码了,再有不懂看注释:

#include<bits/stdc++.h>
using namespace std;
int T;
int n,m;
int a[1001][1001];
int dp[1001][1001];//dp核心数组 
inline int read()//快读不解释 
{
    int f=1,x=0;
    char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
int main()
{
    T=read();//数据组数 
    for(int ix=1;ix<=T;ix++)
    {
        memset(dp,0,sizeof(dp));//dp初值 
        n=read(),m=read();//读入n,m 
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                a[i][j]=read();//读入宝藏数 
            }
        }
        for(int i=1;i<=n;i++)
            for(int j=m;j>=1;j--)//从右上到左下,形成最小反链 
                dp[i][j]=max(dp[i-1][j+1]+a[i][j],max(dp[i-1][j],dp[i][j+1]));//dp方程 
        printf("%lld\n",dp[n][1]);//每次输出左下角的数值 
    }
     
    return 0;
}

  

 

posted @ 2019-04-12 22:34  CZD648  阅读(289)  评论(0编辑  收藏  举报
Live2D