DP(第二版)

第一版请见:直通

话不多说,直接上题

1.P1040 加分二叉树

直通

思路:

  已知中序遍历,相当于一段区间了,所以我们枚举一个k,如果以k为根节点,能够将分数更新,那么这段区间的根节点就置为k,最后dp[1][n]就是得分;

  核心代码:

	for(int i=n-1; i>=1; i--)
		for(int j=i+1; j<=n; j++)
			for(int k=i; k<=j; k++) 
				if(dp[i][k-1]*dp[k+1][j]+a[k]>dp[i][j]) 
					p[i][j]=k,dp[i][j]=dp[i][k-1]*dp[k+1][j]+a[k];

坑点:

  别忘了dp数组的初始值为1,不然会“爆零”

上代码:

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

const int M = 33;
int n;
int a[M],p[M][M],dp[M][M];

void print(int l,int r) {
    if(l>r) return;
    printf("%d ",p[l][r]);
    print(l,p[l][r]-1);
    print(p[l][r]+1,r);
}

int main() {
    scanf("%d",&n);
    for(int i=0; i<=n; i++)
        for(int j=0; j<=n; j++)
            dp[i][j]=1;
    for(int i=1; i<=n; i++) {
        scanf("%d",&a[i]);
        dp[i][i]=a[i];
        p[i][i]=i;
    }
    for(int i=n-1; i>=1; i--)
        for(int j=i+1; j<=n; j++)
            for(int k=i; k<=j; k++) 
                if(dp[i][k-1]*dp[k+1][j]+a[k]>dp[i][j]) 
                    p[i][j]=k,dp[i][j]=dp[i][k-1]*dp[k+1][j]+a[k];
    printf("%d
",dp[1][n]);
    print(1,n);
    return 0;
}
View Code

2.P1052 过河

直通

思路:

  枚举左端点i,能够跳的步数j,以及st数组(存储是否有石头)

      那么转移方程就是:

        dp[i]=min(dp[i],dp[i-j]+st[i]);

  又因为是取min,所以需要进行初始化

坑点:

  题目中说道:当青蛙跳到或跳过坐标为L的点时,就算青蛙已经跳出了独木桥

  所以我们最后进行输出的时候不能够只输出dp[l],而要在dp[l~l+t]之间进行取min

上代码:

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

const int Mod = 2520; //1~10的最小公倍数,是路径压缩的关键!
//因为2520步是它们的倍数,所以一定会直接跳过去,不用管是用多么大的步数跳的 
const int L = 2333333; 
int l,s,t,m,ans;
int a[L],y[L],st[L],dp[L];
//y[] : 压缩后每块石子之前需要跳的步数 

int main() {
    scanf("%d%d%d%d",&l,&s,&t,&m);
    ans=m; //最多跳过m块石头 
    for(int i=1; i<=m; i++) scanf("%d",&a[i]);
    sort(a+1,a+1+m);
    for(int i=1; i<=m; i++) y[i]=(a[i]-a[i-1])%Mod; //状压 
    for(int i=1; i<=m; i++) a[i]=a[i-1]+y[i],st[a[i]]=1; //重新将a进行赋值,并标记石头 
    l=a[m]; //更新l值 
    for(int i=0; i<=l+m; i++) dp[i]=m; //赋对于该题来说的最大值m 
    dp[0]=0; //起点处不含石头 
    for(int i=s; i<l+t; i++)
        for(int j=s; j<=t; j++) //枚举跳的步数 
            if(i-j>=0) dp[i]=min(dp[i],dp[i-j]+st[i]); //dp转移方程 
    for(int i=l; i<l+t; i++) ans=min(ans,dp[i]); //最远跳到l+t 
    printf("%d",ans);
    return 0;
}
View Code

 

原文地址:https://www.cnblogs.com/zxqxwnngztxx/p/7791509.html