You Are the One(HDU 4283)

题意:

给出n个数,每个数字需要都需要进栈然后出栈。
第i个数出栈需要的花费是(i-1)*第i个出栈的权值
求所有数出栈需要的最小花费是多少?

分析:

栈能干什么呢,能把一个数字放进去然后过好久再放出来,但是把这个数字压进去的话压在更底下的数字就出不来了。
看起来挺复杂的= =
但是这题用不到这些东西

虽然说是栈,但是把每次清空栈之间看做一段区间,这些区间可以发现是相互独立存在的。而且这个区间里面怎么操作跟之后的结果无关。所以说可以考虑区间DP。

首先是状态的定义

这个大概还是挺套路的东西…dp[i][j]表示 i,j都出栈的最小值。
但是发现这里那个(i-1)挺麻烦的…而且之后这个[i,j]还可能前后移动然后改变,修改起来就很麻烦。方便起见,当做这个区间的开头就是1。
所以说dp[i][j]就是区间dp[i,j]都出栈所需要的最小花费,并且只有区间i,j

然后是找答案

这里的答案显然是dp[1,n]…这个大概是真的很套路的东西。

然后是初始化

dp[i][i]=a[i],求最小值,剩下的赋值成inf。

然后是关键的状态转移

对于每个子任务,求区间[l,r]的dp值。
想法还是把区间劈成两部分。但是有一个问题,如果只考虑两个区间先后顺序的话,那可能会出现把翻过一次的区间又翻一次。简单来说就是,栈没法完成的却转移进去了。(蒟蒻的我似乎实现不了只考虑区间先后来转移的方法)
然后这里转移有个很巧妙的地方就是,首先枚举[i,j],然后枚举k∈[i,j],意思是第l个数字位置在k,那么转移方程就可以写成
dp[i][j]=min{dp[i+1][k]+dp[k+1][j]+a[i] (k-i)+(sum[j]-sum[k])(k-i+1)}

#include<bits/stdc++.h>
#define M 105
#define pb push_back
#define ll long long
#define inf 1000000000 
using namespace std;

void read(int &x){
    x=0; char c=getchar();
    for (; c<'0'; c=getchar());
    for (; c>='0'; c=getchar())x=(x<<3)+(x<<1)+(c^'0');
}

int a[M],sum[M],dp[M][M];

int main(){
    int ca,i,l,k,n,t,j;
    read(ca);
    for (t=1; t<=ca; t++){
        read(n);
        for (i=1; i<=n; i++)read(a[i]),sum[i]=sum[i-1]+a[i],dp[i][i]=0;
        for (i=1; i<=n; i++){
            for (j=i+1; j<=n; j++)dp[i][j]=inf;
        }
        for (l=1; l<=n; l++){
            for (i=1; i+l<=n; i++){
                j=i+l;
                for (k=i; k<=j; k++)dp[i][j]=min(dp[i][j],dp[i+1][k]+dp[k+1][j]+a[i]*(k-i)+(sum[j]-sum[k])*(k-i+1));
            }
        }
        printf("Case #%d: %d\n",t,dp[1][n]);
    }
    return 0;
}

反思

大概就是看了题解没自己想出来…,但是真的让我想下去也不一定能想到用k表示i的位置而是死磕在区间先后上了。
不过机房有位dalao似乎用的就是那个转移方法。所以还是讲一下。
同样是以dp[i][j] 表示区间[i,j]不考虑之前出栈的人之后的最小花费。
首先dp[i][j]赋初值为 a[i]*(j-i)+dp[i+1][j]
这种情况的话就是把i放在最后面然后[i+1,j]放在前面的情况。
之后同样的枚举k,然后的转移方法是dp[i][j]=min{dp[i][k]+dp[k+1][j]+(k-i+1)*(sum[j]-sum[k])};
然后仔细考虑一下的确是应该把所有最优情况都包括进去了。首先第一个赋值是考虑i是最后一个的情况,之后枚举k,每次枚举也相当于枚举第k个作最后一个(其实也不一定…?但是就是区间最优的最后一个)

总之。我太弱了…这里写图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值