【dp】教主的花园

10 篇文章 0 订阅

题目链接

题目

教主有着一个环形的花园,他想在花园周围均匀地种上n棵树,但是教主花园的土壤很特别,每个位置适合种的树都不一样,一些树可能会因为不适合这个位置的土壤而损失观赏价值。

教主最喜欢3种树,这3种树的高度分别为10,20,30。教主希望这一圈树种得有层次感,所以任何一个位置的树要比它相邻的两棵树的高度都高或者都低,并且在此条件下,教主想要你设计出一套方案,使得观赏价值之和最高。

题解

一道很有意思的dp题
模型一:

dp[i][j][k]表示前i个位置(不包括位置1),在第i个位置放j(10,20,或30),k表示上升或下降(1 or 0) 时所get的最大答案
最后和位置1的美观程度跑一遍就好啦

代码:

#include<cstdio>
#include<iostream>
#define N 100010
using namespace std;
int n,p,q,o,ans,dp[N][4][2],a[4];
int main(){
    freopen("data.txt","r",stdin);
    scanf("%d",&n);
    scanf("%d%d%d",&p,&q,&o);
    for(int i=2;i<=n;i++){
        scanf("%d%d%d",&a[1],&a[2],&a[3]);
        dp[i][1][1]=max(dp[i-1][2][0],dp[i-1][3][0])+a[1];
        dp[i][2][0]=dp[i-1][1][1]+a[2];
        dp[i][2][1]=dp[i-1][3][0]+a[2];
        dp[i][3][0]=max(dp[i-1][1][1],dp[i-1][2][1])+a[3];
    }
    ans=max(ans,dp[n][1][1]+max(q,o));
    ans=max(ans,dp[n][2][0]+p);
    ans=max(ans,dp[n][2][1]+o);
    ans=max(ans,dp[n][3][0]+max(p,q));
    printf("%d\n",ans);
}

模型二:

dp[i][j][k]表示前i棵树已种的情况下,且第i棵树种第k种树,第i-1棵树种第j种树,的最大价值
状态转移方程详见程序内。注意由于第一棵树是有后效性的,会影响第n棵树。所以我们需要枚举第一棵树。

这里我说一下我一开始的想法
记录一个struct

struct node{
    int v,p;
}f[N][4][4];

v是值,p是第一个取得位置(10或20或30)
最后再首尾比较

if(i==n){
    if(k>j&&j<f[i][k][j].p)ans=max(ans,f[i][k][j].v);
    if(k<j&&j>f[i][k][j].p)ans=max(ans,f[i][k][j].v);
}

发现WA了
因为当1.v==2.v时,不能确定1.p和2.p哪个更优

所以我们需要枚举第一棵树。

代码(from Vin_1999):
其中变量fir即为枚举第一棵树

#include<iostream>  
#include<cstdlib>  
#include<cstring>  
#include<cstdio>
#include<climits>
#define rep(i,s,n) for(int (i)=(s);(i)<=(n);(i)++)  
using namespace std;
const int maxn=100010;
static long long dp[maxn][4][4],fir,n,v[maxn][4];
inline void init()
{
    scanf("%d",&n);
    rep(i,1,n) rep(j,1,3) scanf("%d",&v[i][j]);
}
int main ()  
{  
    freopen("e:/in.txt","r",stdin);
    //freopen("e:/out.txt","w",stdout);
    init();
    long long ans=INT_MIN;
    for(fir=1;fir<=3;fir++)
    {
          //printf("%d:",fir);
        memset(dp,-127,sizeof(dp));
        if(fir==1)
        {
            rep(k,2,3) dp[2][fir][k]=v[1][fir]+v[2][k];
                }
                else if(fir==2)
                {
                    dp[2][fir][3]=v[1][2]+v[2][3];
                    dp[2][fir][1]=v[1][2]+v[2][1];
                }
                else
                {
                    rep(k,1,2) dp[2][fir][k]=v[1][fir]+v[2][k];
                }
        rep(i,3,n) rep(j,1,3) rep(k,1,3)
        {
            if(j<k)
            {
                for(int p=j+1;p<=3;p++)
                  dp[i][j][k]=max(dp[i][j][k],dp[i-1][p][j]+v[i][k]);
            }
            else if(j>k)
            {
                for(int p=j-1;p;p--)
                  dp[i][j][k]=max(dp[i][j][k],dp[i-1][p][j]+v[i][k]);
            }
        }
        /*rep(i,1,n) rep(j,1,3)
        {
            rep(k,1,2) printf("%d ",dp[i][j][k]);
            printf("%d\n",dp[i][j][3]);
                }*/
        if(fir==1)
        {
            ans=max(ans,max(dp[n][1][2],max(dp[n][1][3],dp[n][2][3]))); 
        }
        else if(fir==2)
        {
            rep(p,2,3) ans=max(ans,dp[n][p][1]);
            rep(p,1,2) ans=max(ans,dp[n][p][3]);
        }
        else
        {
            ans=max(ans,max(dp[n][3][2],max(dp[n][2][1],dp[n][3][1])));
        }
    }
    printf("%lld",ans);
    return 0;
}//by llyz-ljy

造数据

#include<cstdio>
#include<iostream>
#include<ctime>
#include<cstdlib>
#define MOD 5
using namespace std;
int n;
int main(){
    freopen("data.txt","w",stdout);
    srand((unsigned)time(NULL));
    while((n=rand()%MOD)%2==1);
    printf("%d\n",n);
    for(int i=1;i<=n;i++)printf("%d %d %d\n",rand()%MOD,rand()%MOD,rand()%MOD);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值