一道有趣的区间DP--HDU4283(通俗易懂,附加栗子)

不知道是听谁说的,这是一道区间DP的入门题,对于区间思想确实是一道入门题,但是DP方程个人觉得还是很难的,反正我这种蒟蒻肯定是想不出来的。嘤
首先感谢资料:https://blog.csdn.net/libin56842/article/details/9722077
来看一下题目:
有一群人,按照给定输出从1到n排好了顺序,准备一个接一个的出去.此外旁边还一个小黑屋(一个类似于栈的东西),可以用来调整顺序.每个人个屌丝值,如果他第K个上场,屌丝值就为a[i]*(k-1),同过一个小黑屋来调整,是的最后总屌丝值最小
了解题意之后,开始看代码吧,在代码中讲比较好 
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define INF 99999999
int num[1000],sum[1000],dp[1000][1000];//num[]用来存放输入,sum[]用来预处理前缀和(后面有解释)
//dp[][]用来表示从i到j的最小花费.
int main(void)
{
    int t;
    cin>>t;
    int casen = 1;
    while(t --)
    {
        memset(dp,0,sizeof(dp));
        memset(sum,0,sizeof(sum));
        memset(num,0,sizeof(num));
        int n;
        cin>>n;
        for(int i = 1 ; i <= n ; i ++)//预处理
        {
            cin>>num[i];
            sum[i] = sum[i-1] + num[i];
        }
        for(int i = 1 ; i <= n ; i ++)
        {
            for(int j = i + 1 ; j <= n ; j ++)
            {
                if(i != j)
                    dp[i][j] = INF;
                else
                    dp[i][j] = 0;
            }
        }
        //这几层循环是区间DP的板子,相信了解一点区间DP的人都应该很清楚,就不做过多解释了,我们主要来讲DP方程
        for(int len = 1 ; len < n ; len ++)
        {
            for(int i = 1 ; i <= n - len ; i ++)
            {
                int j = i + len;
                for(int k = 1 ; k <= j - i + 1 ; k ++)
                    dp[i][j] = min(dp[i][j],dp[i+1][i+k-1]+dp[i+k][j]+k*(sum[j]-sum[i+k-1])+num[i]*(k-1));
            }
        }
        /****************************************
        dp[i][j]表示从第i个人到第j个人这段区间的最小花费(是只考虑这j-i+1个人,不需要考虑前面有多少人) 
        那么对于dp[i][j]的第i个人,就有可能第1个上场,也可以第j-i+1个上场。考虑第K个上场 
        即在i+1之后的K-1个人是率先上场的
        
        {
        为什么i+1之后的K-1个人是率先上场的呢?举个例子 对于给定序列 [1] [2] [3] [4] [5] [6] , 我们取 [2] [3] [4] [5] 这个区间
        那么 也就是求 dp[2][5] 的值,此时i = 2 , j = 5 ,我们令 k = 3。
        根据上述 第[3]之后(包括[3])的 2 个人([4],[5])是率先登场的.
        为什么呢:因为[2]第3个出场,所以[2]一定得进去小黑屋(栈--先进后出)此时后面的[3],[4]一定得出场(可以进小黑屋,即调整[3],[4]的出场顺序)但是一定不能让[5]出场,如果[5]出场那么[2]一定不是第3个出场的.
        具体解释:[2]首先进入栈,然后[3]如果进入栈,此时栈{[3],[2]},这时候[4]一定得出场了,因为如果此时[4]不出场,那么也就是说[4]进入了栈,那么此时栈{[4],[3],[2]},到[5]的时候,不管[5]是否出场,[2]都不能
        第三个出场了,可以慢慢想.
        }
        
,        那么就出现了一个子问题 dp[i+1][i+k-1]表示在第i个人之前上场的 
        对于第i个人,由于是第k个上场的,那么屌丝值便是a[i]*(k-1) 
        其余的人是排在第k+1个之后出场的,也就是一个子问题dp[i+k][j],对于这个区间的人,由于排在第k+1(不包含k+1)个之后,所以整体愤怒值要加上k*(sum[j]-sum[i+k-1])
        {        
        为什么整体的愤怒之要加上k*(sum[j]-sum[i+k-1])呢?
        dp[i+k][j],对于这个区间的人,由于排在第k+1(包含k+1)个之后
        dp[i+k][j]选出来的最优解是没有等k个人的,但是实际上是等了k个人,所以补上来
        还是上面的例子:
        但是改变k,让k = 2
        [2] [3] [4] [5]
         2   3   4   5
         dp[2][5] = min(dp[2][5],dp[3][3](第i个人之前上场的愤怒值最优解)+dp[4][5](第2个人之后上场的愤怒值最优解)+num[2]*(2 - 1)(第2个人第2个上场的愤怒值)+2*(sum[5]-sum[3])([4]号和[5]号因为等待2个人增长的愤怒值))
         dp[3][3] = 0 
         dp[4][5] = 4*1+5*0 = 4 
         num[2]*(2-1) = 2 
         2*(sum[5]-sum[3]) = 4 + 4 + 5 + 5 = 18
         以a * b (a代表屌丝值,b代表出场顺序)综合:dp[2][5] = 3*0+2*1+5*2+4*3 = 24 .所以出场顺序为 [2]: 2 [3]: 1 [4]: 4 [5];3
         ****************************************/
        printf("Case #%d: %d\n",casen++,dp[1][n]);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值