HDU 4283 You Are the One ——区间dp

参考了许多大佬  尤其是https://blog.csdn.net/woshi250hua/article/details/7973824这一篇 ,最后我再加一点我的见解。

大意是 给定一个序列,序列内的人有屌丝值Di,将这个序列进栈,第i个人如果是第k个出栈,那么最后的屌丝总值增加Di * (k-1), 求一个出栈序列使得总屌丝值最小。

这一题用到了一个性质,如果第1个人确定了是第k个出栈,那么第2~k个人一定在他之前出栈,而且k+1~n个人一定在他之后出栈。这个可以随便拿纸模拟一下来得出结论。

然后区间划分的思路就有了,枚举一下k,处理出[l,r]区间l第k个弹出时的最小总屌丝值。最后dp[1][n][1]就是最终解。

代码如下:

#include<iostream>
#include<string.h>
using namespace std;
#define INF (1<<29)
#define ll long long
ll dp[105][105][105],n,a[105],ans; 
ll solve(ll l,ll r,ll k)
{
    if(l>r) return 0; //无意义值直接返回 
    if(l==r) return a[l]*(k-1); //递归到单点,意味着该值意义为“第l个人第k个出场带来的屌丝值” 
    if(dp[l][r][k]!=INF) return dp[l][r][k];  //不为INF代表该值之前走到过,就不用再走了,直接返回该值 
    
    ll qian,hou,qiank,houk,now;
    // 根据之前的结论,我们把区间[l,r]分为三个部分 l、l+1~k、k+1~r   
    
    for(int i=l;i<=r;i++)
    {
        houk=k+i-l+1;//该值houk表示递归区间k+1~r时带入的k 
        qiank=k+i-l;//该值qiank示第l号人出场的序号 
        qian=solve(l+1,i,k);//该值qian表示递归区间l+1~i时返回的最优值 
        hou=solve(i+1,r,houk);//该值hou表示递归区间i+1~r时返回的最优值 
        now=a[l]*(qiank-1);//l号选手的屌丝值 
        dp[l][r][k]=min(dp[l][r][k],qian+hou+now); //更新最优值 
    }
    return dp[l][r][k];
}
int main()
{
    ll i,j,k,l,ans,t,n;
    //cout<<INF<<endl;
    cin>>t;
    l=0;
    while(t--)
    {
        l++;
        cin>>n;
        for(i=1;i<=n;i++) cin>>a[i];
        for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
        for(k=0;k<=n;k++) dp[i][j][k]=INF;//提示这里的INF比较大  不能用memset来赋值  不然会出问题 
        ans=solve(1,n,1);
        cout<<"Case #"<<l<<": "<<ans<<endl;
    }
}

 

posted @ 2019-04-12 20:33  啾啾猫猫  阅读(163)  评论(0编辑  收藏  举报