线性dp(数字三角形模型)

这篇博客介绍了动态规划在解决数字三角形、摘花生和传纸条问题中的应用。通过状态转移方程,展示了如何从不同方向进行优化,以找到最优解。在数字三角形问题中,从上到下或从下到上进行DP;摘花生问题同样使用DP,优化空间复杂度;传纸条问题则考虑了两人同时移动的路径选择。所有问题都通过AC代码进行了实现。
摘要由CSDN通过智能技术生成

目录 

数字三角形

摘花生

 传纸条


数字三角形

题目链接

898. 数字三角形 - AcWing题库

状态表示:来自左上f[i][j]  =  f[i-1][j-1]

                  来自右上f[i][j]  =  f[i-1][j]

                    f[i][j]=max(f[i-1][j],f[i-1][j-1])+w[i][j]

AC代码

#include<bits/stdc++.h>
using namespace std;
const int N=510,INF=0x3f3f3f3f;
int f[N][N],w[N][N];
int n;
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	for(int j=0;j<=i+1;j++)
	f[i][j]=-INF;//结果可以有负数,所以要将两边变为-INF; 
	for(int i=1;i<=n;i++)
	for(int j=1;j<=i;j++)
		cin>>w[i][j];
		f[1][1]=w[1][1];
	for(int i=2;i<=n;i++)
	for(int j=1;j<=i;j++)
		{
			f[i][j]=max(f[i-1][j],f[i-1][j-1])+w[i][j];	
		}
		int ret=-INF;
		for(int i=1;i<=n;i++)//找出第n行的最大值 
		ret=max(ret,f[n][i]);
		cout<<ret<<endl;
	 
	return 0;
}

可以从下往上dp优化

状态表示:f[i][j] = max ( f[i+1][j] , f[i+1][j+1] ) + w[i][j];

此时不需要初始化f[i][j]

#include<bits/stdc++.h>
using namespace std;
const int N=510;
int f[N][N],w[N][N];
int n;

int main(){
    cin>>n;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=i;j++)
        cin>>w[i][j];
    
    for(int i=n;i>=1;i--)
    for(int j=1;j<=i;j++)
        f[i][j]=max(f[i+1][j],f[i+1][j+1])+w[i][j];
    
	
	cout<<f[1][1]<<endl;
    return 0;
}

摘花生

题目链接

1015. 摘花生 - AcWing题库

状态转移

                (i, j)从(i-1, j)即上方过来
                (i, j)从(i, j-1)即左方过来

状态表示:f[i][j] = max ( f[i-1][j] , f[i][j-1] ) + w[i][j];

AC代码

#include<bits/stdc++.h>
using namespace std;
const int N=110;
int w[N][N],f[N][N];
int c,n,m;
int main()
{
	cin>>c;
	while(c--){
		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++)
				f[i][j]=max(f[i-1][j],f[i][j-1])+w[i][j];
	cout<<f[n][m]<<endl;
		} 

	
	return 0;
}

空间优化

for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
		f[j]=max(f[j],f[j-1])+w[i][j];
	cout<<f[m]<<endl;
	memset(f, 0, sizeof f);//每次结束都得初始化 

注:每次结束必须对f[M]初始化 

 由于多组样例,而二维数组解法由于f[0][...]和f[...][0]都为0,所以没有问题。对于一维数组,上一样例的f数组需要清零,否则影响结果

优化后AC代码:

#include<bits/stdc++.h>
using namespace std;
const int N=110;
int w[N][N],f[N];
int c,n,m;
int main()
{
	cin>>c;
	while(c--){
		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++)
			{
				f[j]=max(f[j],f[j-1])+w[i][j];
			}
	 cout<<f[m]<<endl;
	 memset(f, 0, sizeof f);//每次都得初始化 
	
		} 

	
	return 0;
}

传纸条

题目链接

275. 传纸条 - AcWing题库

解题思路:可以看做两人同时由起点出发,同时迈出每一步可右可下,走两条不相交的路

状态表示: f[k][i][j] = max ( f[k][i][j] , f[k - 1][i - a][j - b] + t );

                    k表示走到第k步,i表示第一个人在前k步中向右走的步数(即第一个人所在位置的横坐标)

                     j表示第二个人在前k步中向右走的步数(即第二个人所在位置的横坐标)

                     t表示在第k步是产生的总价值

AC代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 55;

int n, m;
int g[N][N];
int f[N * 2][N][N];

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            scanf("%d", &g[i][j]);

    for (int k = 2; k <= n + m; k ++ )//k表示走到第k步; 
        for (int i = max(1, k - m); i <= n && i < k; i ++ )
		//i表示第一个人在前k步中向右走的步数(即第一个人所在位置的横坐标) 
            for (int j = max(1, k - m); j <= n && j < k; j ++ )
			//j表示第二个人在前k步中向右走的步数(即第二个人所在位置的横坐标)
                
				for (int a = 0; a <= 1; a ++ )
                    for (int b = 0; b <= 1; b ++ )
                    //上面两层循环为两人第k步是向右还是向下的情况
                	//一共4种(一右二下,一右二右,一下二下,一下二右)
                    {
                        int t = g[i][k - i];//第k步时第一个人产生的价值						
			                        if (i != j || k == 2 || k == n + m)
									//k==2表示起点(1,1)  k==n+m表示终点(n,m) 
			                        {
			                            t += g[j][k - j];//第k步的总价值 
			                            f[k][i][j] = max(f[k][i][j], f[k - 1][i - a][j - b] + t);
										//更新两人分别走了k步产生的最大总价值 
			                        }
					}
                    

    printf("%d\n", f[n + m][n][n]);

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值