文章目录
一、数字三角形母体
题意:我们从上面开始取数,然后我们要求的数的和要求是最大的,问我们和最大是多少。
思路:这个题我们用闫氏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;
}