bzoj 3997 & 洛谷 3974 组合数学 题解(建图,图论,DP)

本文介绍了一种基于矩阵寻宝问题的动态规划算法。通过构建有向无环图(DAG),并使用DP求解最大独立集问题,实现最优路径选取,确保能够以最少次数收集所有财宝。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原题链接:
bzoj
洛谷

题意简述

给定一个 n ∗ m n*m nm的矩阵,每个点上有一定量的财宝。每次从左上往右下走,只能往右或下走。走一个取走一个单位的财宝,多少次取完?

数据

输入
n m//描述矩阵,n,m<=1000
a11 a12 ... a1m
a21 a22 ... a2m
...
an1 an2 ... anm
//给定矩阵。0<=每个权值<=1000000
输出

一行,一个整数,答案

样例

输入
1

3 3

0 1 5

5 0 0

1 0 0
输出
10

思路

我们把矩阵中每个点建图,对于它右边和下边的点,都连一条有向边。然后就是一个 D A G DAG DAG了,显然。然后就是非常经典的最小链覆盖问题了。它的答案等于最大独立集。

那么。。。如何求最大独立集?对于这个矩阵来说,我们是非常好 D P DP DP的,因为图建的很有规律。所以我们完全珂以只依靠下标,而不用真实存图。我们用 d p [ i ] [ j ] dp[i][j] dp[i][j]表示从 ( i , j ) (i,j) (i,j) ( n , m ) (n,m) (n,m)的子矩阵中建一个子图,的最大独立集数。如何转移呢?
我们会发现,没法转移。原因是方向不对,因为从 ( i , j ) (i,j) (i,j) ( n , m ) (n,m) (n,m)刚好顺应了建图的方向,所以答案只可能为 1 1 1
所以要从左下到右上,也就是从 ( n , 1 ) (n,1) (n,1) ( 1 , m ) (1,m) (1,m)才是正确的方向。方程就很好推了,代码:

#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
{
    #define N 1010
    #define int long long
    #define MEM(x,a) memset(x,a,sizeof(x))
    #define FK(x) MEM(x,0)
    int n,m;
    int a[N][N];
    void R1(int &x)
    {
        x=0;char c=getchar();int f=1;
        while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
        while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=(f==1)?x:-x;
    }
    void Input()
    {
        R1(n),R1(m);
        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=m;++j)
            {
                R1(a[i][j]);
            }
        }
    }
 
    int dp[N][N];
    int max3(int a,int b,int c)
    {
        return max(a,max(b,c));
    }
    void Soviet()
    {
        FK(dp);
        for(int i=n;i>=1;--i)
        {
            for(int j=1;j<=m;++j)
            {
                dp[i][j]=max3(dp[i+1][j],dp[i][j-1],dp[i+1][j-1]+a[i][j]);
            }
        }
        printf("%lld\n",dp[1][m]);
    }
    void IsMyWife()
    {
        if (0)
        {
            freopen("","r",stdin);
            freopen("","w",stdout);
        }
        int t;R1(t);
        while(t--)
        {
            Input();
            Soviet();
        }
    }
    #undef int //long long
};
int main()
{
    Flandle_Scarlet::IsMyWife();
    return 0;
}

回到总题解界面

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值