动态规划01

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

数字三角形 Number Triangles

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

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

动态规划,我们设 d p i j dpij dpij 表示从 ( 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 dpij=max(dpi-1j,dpi-1j-1)+aij dpij=max(dpi−1j,dpi−1j−1)+aij

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

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 dpRC dpRC

推导状态转移方程,由于题目限制, ( 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 dpij=max(dpi-1j,dpij-1)+wij dpij=max(dpi−1j,dpij−1)+wij

还可以进行优化,由于第 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 dpij dpij 仍然是从 ( 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 dpij=min(dpi-1j,dpij-1)+wij dpij=min(dpi−1j,dpij−1)+wij

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

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 dpij dpij 来表示从 ( 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 dpij=max(dpi-1j,dpij-1)+wij dpij=max(dpi−1j,dpij−1)+wij

不同的是这个题目要求走两次,那么以 d p i 1 j 1 i 2 j 2 dpi1j1i2j2 dpi1j1i2j2 来表示分别走到 ( 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 dpki1i2 dpki1i2 来表示分别走到 ( 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();
	}
}
相关推荐
先吃饱再说6 小时前
判断回文字符串,从一行代码到双指针优化
算法
黄敬峰8 小时前
深入理解算法核心:从递归思想、数组扁平化到快速排序
算法
得物技术10 小时前
从狂野代码到按目标生产:得物推荐 AI Harness 的工程化实践|AICon 演讲整理
人工智能·算法·架构
AI小老六13 小时前
SkillOpt 架构拆解:把 Skill 文本当参数,用执行轨迹训练 Agent
后端·算法·ai编程
胡萝卜术14 小时前
从“分数打架”到“排名投票”:为什么你的ChatBI必须用RRF?
算法·设计模式·面试
Asize14 小时前
初识DFS 与 BFS:递归、队列与图遍历
算法
罗西的思考1 天前
机器人 / 强化学习】HIL-SERL:人类在环驱动的具身智能进化框架
人工智能·算法·机器学习
美团技术团队1 天前
LongCat 开源 VitaBench 2.0:长期动态智能体基准新标杆
人工智能·算法
To_OC2 天前
LC 207 课程表:刚学图论那会儿,我连这是拓扑排序都没看出来
javascript·算法·leetcode