动态规划01

线性 dp--数字三角形模型

数字三角形 Number Triangles

题目链接:
P1216 [IOI 1994 / USACO1.5] 数字三角形 Number Triangles - 洛谷
思路:

把这个三角抽象为二维数组。

动态规划,我们设 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示从 ( 1 , 1 ) (1,1) (1,1) 到 ( i , j ) (i,j) (i,j) 的所有方案的集合的最大值。这个点可以由 ( i − 1 , j ) (i-1,j) (i−1,j) 或者 ( i − 1 , j − 1 ) (i-1,j-1) (i−1,j−1) 走过来。

所以状态转移方程为 d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − 1 ] ) + a [ i ] [ j ] dp[i][j]=max(dp[i-1][j],dp[i-1][j-1])+a[i][j] dp[i][j]=max(dp[i−1][j],dp[i−1][j−1])+a[i][j]。

答案就是最下面一层的最大值。
代码:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define endl '\n'
const int N=1050;
int a[N][N];
int dp[N][N];
void solve()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=i;j++)
        {
            cin>>a[i][j];
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=i;j++)
        {
            dp[i][j]=max(dp[i-1][j],dp[i-1][j-1])+a[i][j];
        }
    }
    int ans=-1e18;
    for(int i=1;i<=n;i++)ans=max(ans,dp[n][i]);
    cout<<ans<<endl;
}
signed main(){
	IOS
	int t=1;
	while(t--)
	{
		solve();
	}
}

U280643 摘花生

题目链接:
U280643 摘花生 - 洛谷
思路:

数字三角形模型,我们可以开一个二维数组 dp[i][j] 来表示从 ( 1 , 1 ) (1,1) (1,1) 走到 ( i , j ) (i,j) (i,j) 的所有路线所能采摘到花生数量的最大值,最后答案为 d p [ R ] [ C ] dp[R][C] dp[R][C]。

推导状态转移方程,由于题目限制, ( i , j ) (i,j) (i,j) 点只能从 ( i − 1 , j ) (i-1,j) (i−1,j) 或 ( i , j − 1 ) (i,j-1) (i,j−1) 到达,所以最终方程为 d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] ) + w [ i ] [ j ] dp[i][j]=max(dp[i-1][j],dp[i][j-1])+w[i][j] dp[i][j]=max(dp[i−1][j],dp[i][j−1])+w[i][j]。

还可以进行优化,由于第 i 层只依赖于第 i 层和第 i-1 层,所以可以优化为一维数组。在当前层可以由上一层的该列到达或者由当前层的前列到达。
代码:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define endl '\n'
const int N=150;
int w[N][N];
int dp[N][N];
void solve()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            cin>>w[i][j];
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            dp[i][j]=max(dp[i-1][j],dp[i][j-1])+w[i][j];
        }
    }
    cout<<dp[n][m]<<endl;
}
/*一维数组优化空间
//用dp[m]来表示当前计算行第m列的最大值,当计算结束后直接输出dp[m]即可
//记得及时清理数组
void solve()
{
    int n,m;
    cin>>n>>m;
    int w;
    memset(dp,0,sizeof dp);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            cin>>w;
            dp[j]=max(dp[j-1],dp[j])+w;
        }
    }
    cout<<dp[m]<<endl;
}
*/
signed main(){
	IOS
	int t;
	cin>>t;
	while(t--)
	{
		solve();
	}
}

最低通行费

题目链接:
T118735 [ybt1287]最低通行费 - 洛谷
思路:

题目限制了不能超过 2 n − 1 2n-1 2n−1 这其实就是不能往回走。所以我们的状态表示 d p [ i ] [ j ] dp[i][j] dp[i][j] 仍然是从 ( 1 , 1 ) (1,1) (1,1) 走到 ( i , j ) (i,j) (i,j) 的所有方案的集合的最小值。
( i , j ) (i,j) (i,j) 点只能从 ( i − 1 , j ) (i-1,j) (i−1,j) 或 ( i , j − 1 ) (i,j-1) (i,j−1) 到达,所以最终方程为 d p [ i ] [ j ] = m i n ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] ) + w [ i ] [ j ] dp[i][j]=min(dp[i-1][j],dp[i][j-1])+w[i][j] dp[i][j]=min(dp[i−1][j],dp[i][j−1])+w[i][j]。

求最小值,那么初始化为最大值。
代码:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define endl '\n'
const int N=1050;
int w[N][N];
int dp[N][N];
void solve()
{
    int n;
    cin>>n;
    memset(dp,0x3f,sizeof dp);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            cin>>w[i][j];
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(i==1&&j==1)dp[1][1]=w[1][1];
            else dp[i][j]=min(dp[i-1][j],dp[i][j-1])+w[i][j];
        }
    }
    cout<<dp[n][n];
}
signed main(){
	IOS
	int t=1;
	while(t--)
	{
		solve();
	}
}

方格取数

题目链接:
P1004 [NOIP 2000 提高组] 方格取数 - 洛谷
思路:

如果只走一次使用 d p [ i ] [ j ] dp[i][j] dp[i][j] 来表示从 ( 1 , 1 ) (1,1) (1,1) 走到 ( i , j ) (i,j) (i,j) 所有路径集合的最大值。状态转移公式为 d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] ) + w [ i ] [ j ] dp[i][j]=max(dp[i-1][j],dp[i][j-1])+w[i][j] dp[i][j]=max(dp[i−1][j],dp[i][j−1])+w[i][j]。

不同的是这个题目要求走两次,那么以 d p [ i 1 ] [ j 1 ] [ i 2 ] [ j 2 ] dp[i1][j1][i2][j2] dp[i1][j1][i2][j2] 来表示分别走到 ( i 1 , j 1 ) (i1,j1) (i1,j1) 、 ( i 2 , j 2 ) (i2,j2) (i2,j2) 的路径最大值。四维不太好做,可以发现只有当 i 1 + j 1 = i 2 + j 2 = k i1+j1=i2+j2=k i1+j1=i2+j2=k 时才可能踩到重复的格子。

这样我们就可以用三维数组 d p [ k ] [ i 1 ] [ i 2 ] dp[k][i1][i2] dp[k][i1][i2] 来表示分别走到 ( i 1 , k − i 1 ) (i1,k-i1) (i1,k−i1) 、 ( i 2 , k − i 2 ) (i2,k-i2) (i2,k−i2) 的路径最大值。

代码:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define endl '\n'
const int N=15;
int w[N][N];
int dp[2*N][N][N];
void solve()
{
    int n;
    cin>>n;
    int a,b,c;
    while(cin>>a>>b>>c){
        w[a][b]=c;
    }
    for(int k=2;k<=n*2;k++){
        for(int i1=1;i1<=n;i1++)
        {
            for(int i2=1;i2<=n;i2++)
            {
                int j1=k-i1,j2=k-i2;
                if(j1>0&&j2>0&&j1<=n&&j2<=n){
                    int m=w[i1][j1];
                    if(i1!=i2)m+=w[i2][j2];
                    int &x=dp[k][i1][i2];
                    x=max(x,dp[k-1][i1-1][i2-1]+m);
                    x=max(x,dp[k-1][i1-1][i2]+m);
                    x=max(x,dp[k-1][i1][i2-1]+m);
                    x=max(x,dp[k-1][i1][i2]+m);
                }
            }
        }
    }
    cout<<dp[2*n][n][n];
}
signed main(){
	IOS
	int t=1;
	while(t--)
	{
		solve();
	}
}

传纸条

题目链接:
P1006 [NOIP 2008 提高组] 传纸条 - 洛谷
思路:

这个题目同上面方格取数的思路,这里的好感度可以看作是格子里的数字,每个数字只能用一次。
代码:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define endl '\n'
const int N=55;
int w[N][N];
int dp[2*N][N][N];
void solve()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            cin>>w[i][j];
        }
    }
    for(int k=2;k<=n+m;k++){
        for(int i1=1;i1<=n;i1++)
        {
            for(int i2=1;i2<=n;i2++)
            {
                int j1=k-i1,j2=k-i2;
                if(j1>0&&j2>0&&j1<=m&&j2<=m){
                    int t=w[i1][j1];
                    if(i1!=i2)t+=w[i2][j2];
                    int &x=dp[k][i1][i2];
                    x=max(x,dp[k-1][i1-1][i2-1]+t);
                    x=max(x,dp[k-1][i1-1][i2]+t);
                    x=max(x,dp[k-1][i1][i2-1]+t);
                    x=max(x,dp[k-1][i1][i2]+t);
                }
            }
        }
    }
    cout<<dp[m+n][n][n];
}
signed main(){
	IOS
	int t=1;
	while(t--)
	{
		solve();
	}
}
相关推荐
qyzm16 小时前
天梯赛练习(3月13日)
开发语言·数据结构·python·算法·贪心算法
逆境不可逃16 小时前
LeetCode 热题 100 之 64. 最小路径和 5. 最长回文子串 1143. 最长公共子序列 72. 编辑距离
算法·leetcode·动态规划
CoderCodingNo17 小时前
【GESP】C++五级练习题 luogu-P1182 数列分段 Section II
开发语言·c++·算法
放下华子我只抽RuiKe517 小时前
机器学习全景指南-直觉篇——基于距离的 K-近邻 (KNN) 算法
人工智能·gpt·算法·机器学习·语言模型·chatgpt·ai编程
kisshuan1239617 小时前
[特殊字符]【深度学习】DA3METRIC-LARGE单目深度估计算法详解
人工智能·深度学习·算法
sali-tec17 小时前
C# 基于OpenCv的视觉工作流-章33-Blod分析
图像处理·人工智能·opencv·算法·计算机视觉
Eward-an18 小时前
LeetCode 239. 滑动窗口最大值(详细技术解析)
python·算法·leetcode
一叶落43818 小时前
LeetCode 50. Pow(x, n)(快速幂详解 | C语言实现)
c语言·算法·leetcode
皙然18 小时前
彻底吃透红黑树
数据结构·算法
t1987512819 小时前
TOA定位算法MATLAB实现(二维三维场景)
开发语言·算法·matlab