DP真是难,这该怎么办???
dp,每个OIer都要经历的一大关卡,其中,dp的进阶:状压dp,树形dp等等就算是省选选手也会头疼好一阵子
我们今天只会介绍基础dp
1、基础dp
基础dp,即线性dp,也就是可以用类似ans+=min(ans,xxxxxxx)等的dp格式,这种dp思考起来比较简单
我们从例题入手,开始讲解:
1、数字三角形ioi94
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
上图给出了一个数字三角形。从三角形的顶部到底部有很多条不同的路径。对于每条路径,把路径上面的数加起来可以得到一个和,你的任务就是找到最大的和。
注意:路径上的每一步只能从一个数走到下一层上和它最近的左边的那个数或者右边的那个数
输入
输入的是一行是一个整数N (1 < N <= 100),给出三角形的行数。下面的N行给出数字三角形。数字三角形上的数的范围都在0和100之间。
输出
输出最大的和。格式: max=最大值
样例输入[复制]
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
样例输出[复制]
max=30
这道题用搜索来做是一种方法,但搜索不能AC,会TLE(所以搜索不能满足某些大佬想要AK IOI的需求)于是这里我们来讲一下如何用dp来做
【思路】
dp要满足的一个重要的点是无后效性,只要无后效性时,多半可以用dp(或贪心)来做,这里,对于每一个点,从上往下(或从下往上)来运行时,不会对下面(或上面)的点造成影响,所以,我们可以考虑,对于每一行中的点,我们可以考虑计算这一行中所有点的最大值,即是:
5 a[1][1]
3 2 a[2][1],a[2][2]
1 4 2 a[3][1],a[3][2],a[3][3];
对于这组例子,我们可以将它分解为
5 5 3 3 2 2
3 2 1 4 2
这5个子问题,对于每个子问题,我们都可以很轻松的由上面的值来算出这个三角形的最大值
所以会有:
f[2][1]=a[2][1]+f[1][1];
f[2][2]=a[2][2]+f[1][1];
f[3][1]=a[3][1]+f[2][1];
f[3][1]=a[3][1]+max(f[2][1],f[2][2]);
f[2][1]3=a[2][1]+f[1][1];
只需要将这几个状态转移方程化成一般形式扔到for中即可
P.S:从下往上算不用计算边界
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int a[1001][1001],f[1001][1001],n;
int main(){
cin>>n;
for(int i=1;i<=n;i++) for(int j=1;j<=i;j++)
cin>>a[i][j];
f[1][1]=a[1][1];
for(int i=2;i<=n;i++){
for(int j=1;j<=i;j++){
f[i][j]=a[i][j]+max(f[i-1][j-1],f[i-1][j]);
}
}
for(int i=1;i<=n;i++){
if(f[n][1]<f[n][i]) f[n][1]=f[n][i];
}
cout<<"max="<<f[n][1];
return 0;
}
【练习】:
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
上图给出了一个数字三角形。从三角形的顶部到底部有很多条不同的路径。对于每条路径,把路径上面的数加起来可以得到一个和,你的任务就是找到最大的和。
注意:路径上的每一步只能从一个数走到下一层上和它最近的左边的那个数或者右边的那个数,
当然这道题不是这样,加上1个条件了,某一步可以随意跳
输入
输入的是一行是一个整数N (1 < N <= 100),给出三角形的行数。
下面的N行给出数字三角形。数字三角形上的数的范围都在0和100之间。
输出
输出最大的和。格式: max=最大值
样例输入[复制]
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
样例输出[复制]
max=35
【标程】
请自己敲完后再看!!!
#include<iostream>
using namespace std;
int a[101][101],f[101][101][101];
int main()
{
int n,maxn=0;
cin>>n;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=i;j++)
{
cin>>a[i][j];
f[i][j][0]=max(f[i-1][j][0],f[i-1][j-1][0])+a[i][j];
f[i][j][1]=max(f[i-1][j][1],f[i-1][j-1][1])+a[i][j];
for(int k=1;k<=i;k++){
f[i][j][1]=max(f[i-1][k][0]+a[i][j],f[i][j][1]);
}
}
for(int i=1;i<=n;i++)
{
if(maxn<=f[n][i][1])
maxn=f[n][i][1];
}
}
cout<<"max="<<maxn;
return 0;
}