动态规划之数字三角形

一、数字三角形母体

题意:我们从上面开始取数,然后我们要求的数的和要求是最大的,问我们和最大是多少。

思路:这个题我们用闫氏dp分析法。首先我们从状态表示来看,我们可以规定集合是走到a[i][j]为止总和最大是多少。然后属性就是Max。我们在来看状态计算,我们可以划分为两个部分,它只能由f[i-1][j-1]和f[i-1][j]转移过来。最后我们遍历一遍最后一行求一下最大值即可。

#include <bits/stdc++.h>
using namespace std;

const int N = 510, INF = 1e9;
int a[N][N],f[N][N];

int main()
{
    int n;
    scanf("%d", &n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
            scanf("%d",&a[i][j]);
    
    for(int i=0;i<n;i++)
        for(int j=0;j<=i+1;j++)
            f[i][j]=INF;
            
    f[1][1]=a[1][1];
    for(int i=2;i<=n;i++)
        for(int j=1;j<=i;j++)
            f[i][j]=max(f[i-1][j-1]+a[i][j],f[i-1][j]+a[i][j]);
            
    int ans=-INF;
    for(int i=1;i<=n;i++) ans=max(ans,f[n][i]);
    
    printf("%d\n",ans);
    
    return 0;
}

二、数字三角形扩展题

1、摘花生问题
题意:给我们一个n*m的矩阵,然后让我们从左上角走到右下角,让我我们计算和的最大值,不能走回头路。

思路:我们可以类比数字三角形的那个题。首先我们进行状态表示,集合就是走到a[i][j]为止和的最大值,属性就是Max。然后我们进行状态计算,我们可以发现状态只能由f[i-1][j]和f[i][j-1]转移过来。那我们就分成这两种情况,然后我们可以每次求两个的最大值,然后加上当前的a[i][j]即可,最后输出f[n][m]即可。

	#include <bits/stdc++.h>
using namespace std;

const int N = 110;
int w[N][N];
int f[N][N];

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int T;
	cin>>T;
	while(T--)
	{
		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++)
				f[i][j]=max(f[i-1][j],f[i][j-1])+w[i][j];
		
		cout<<f[n][m]<<endl;
	}
	return 0;
}

2、最低通行费
题意:给我们一个nn的矩阵,然后我们要在2n-1步之内从左上角走到右下角,然后求一个和的最小值。

思路:我们可以发现在不走回头路的情况下我们都需要2*n-1步,所以我们不能走回头路,这样的话就变成了和上一个题差不多的题了。

#include <iostream>
#include <algorithm>
using namespace std;

const int N = 101;
int a[N][N];
int f[N][N];
int INF = 0x3f3f3f3f;

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)	
		for(int j=1;j<=n;j++)	
			cin>>a[i][j];

	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(i==1&&j==1)	f[i][j]=a[i][j];
			else
			{
				f[i][j]=INF;
				if(i!=1)	f[i][j]=min(f[i][j],f[i-1][j]+a[i][j]);
				if(j!=1)	f[i][j]=min(f[i][j],f[i][j-1]+a[i][j]);
			}	
		}
	}
	cout<<f[n][n]<<endl;
	return 0;	
}

3、传纸条
题意:给我们一个n*m的矩阵,让后从左上角走到右下角,然后再从右下角走到左上角,走两遍求走得和的最大值,这里注意如果你在第一遍走过了第二遍就不能再加上那个数了。

思路:我们还是先分析一下这个题的话我们可以分开走也可以一块走。我们这里采用的是一块走的方法。首先我们进行状态的表示,集合就是分别从(1,1)和(1,1)走到(i1,j1)和(i2,j2)的最大和是多少,k表示的是同步的。一开始是四维的f[i1][j1][i2][j2],那我们可以想一想如何去化简掉一维,我们可以用k表示步数,然后只用i1,和i1表示就行了,那么相应的j1就是k-i1,j2就是k-i2,这样话我们就可以用三维来表示了。属性就是Max,然后状态计算就是分成四种情况
在这里插入图片描述
这样的话我们只需要,求一下四种情况的最大值,然后如果两条路走到同一格的话,我们就加一个就可以,不是的话就加上两个。那什么情况会走到一个呢,因为总不数是相同的也就是说当i1i2或者j1j2的时候就会相同。

#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 55;
int w[N][N];
int f[N+N][N][N];

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	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>=1&&j1<=m&&j2>=1&&j2<=m)
				{
					int t=w[i1][j1];
					if(i1!=i2)	t+=w[i2][j2];
					int &x = f[k][i1][i2];
					x = max(x,f[k-1][i1-1][i2-1]+t);
					x = max(x,f[k-1][i1-1][i2]+t);
					x = max(x,f[k-1][i1][i2-1]+t);
					x = max(x,f[k-1][i1][i2]+t);
				}
			}
		}
	}
	cout<<f[n+m][n][n]<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值