动态规划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();
	}
}
相关推荐
SmartBrain2 小时前
技术总结:VLLM部署Qwen3模型的详解
开发语言·人工智能·算法·vllm
weixin_477271692 小时前
第四正:关键(马王堆帛书《老子》20)
人工智能·算法·图搜索算法
玄〤2 小时前
枚举问题的两大利器:深度优先搜索(DFS)与下一个排列(Next Permutation)算法详解(Java版本)(漫画解析)
java·算法·深度优先·dfs
weixin_477271692 小时前
第三正:结构(马王堆帛书《老子》2)
算法·图搜索算法
uesowys2 小时前
算法开发指导-数据结构-Tree
数据结构·算法·
小冻梨6662 小时前
ABC445 C - Sugoroku Destination题解
c++·算法·深度优先·图论·
啊阿狸不会拉杆2 小时前
《计算机视觉:模型、学习和推理》第 6 章-视觉学习和推理
人工智能·学习·算法·机器学习·计算机视觉·生成模型·判别模型
道法自然|~2 小时前
BugCTF猪圈密码
算法
52Hz1182 小时前
力扣33.搜索旋转排序数组、153.寻找排序数组中的最小值
python·算法·leetcode