【hdu4283】区间动态规划问题

来源:互联网 发布:唱吧假唱软件 编辑:程序博客网 时间:2024/05/29 18:10

题目编号:

HDU4283


题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=4283


题目描述:

N(N <=  100)个人排成一个队列上舞台,上舞台之前旁边有一个小黑屋(后进先出,其实就是一个栈),队首的人可以选择进小黑屋,或则上舞台。

每个人有一个躁动值值,第i个人的躁动值为a[i],则第i个人的愤怒值为,(K-1) * a[i],k是他最后的出场次序。

求经过这个栈调整后的最终出场顺序,得到所有人的总愤怒值最小。如图:


样例输入:

1 2 3 4 5

样例输出:

20

解释:逆序输出总愤怒值最小,即5*0 + 4*1 + 3*2 +2*3 + 1*4 = 20


题目分析:

 N <= 100,那么基本就是动态规划的方法了,

其实本质就是,第i个人选择进栈或者上场,暴力法的O(2^N),故考虑用DP

100个人,单行线性可列的,

且满足区间独立性,即[l, r]的结果可以分成[l, k] + k + [k + 1, r],故使用区间动态规划。

也就是用整体的思想,【L, R】区间可以看成是【前半区间】+ 【L,R】区间第一个人最后的出场次序 +【后半区间】

具体的,【L,R】区间,第一个人最终出场次序为K时,那么整体的最终结果【L, R】 = 【L + 1, K】 + 偏差1 * K + 偏差2 *【K + 1, R】

如图:



代码中核心:dp[l][r][head]; // 表示区间[i, j],前方已有head个人出去,的最优结果

  1. int dpL = solve(l + 1, k, head);  
  2.         int dpK = (head + k - l) * a[l];  
  3.         int dpR = solve(k + 1, r, head + k - l + 1);  
  4.         int sum = dpL + dpK + dpR;  
  5.         dpMin = dpMin < sum ? dpMin : sum;  

[l, r]的第一个元素在k位置时

计算dpL:第一个元素变成l+1, 最后是k,且原来第一个元素走了,那l + 1又变成新的第一个元素,那么l+1前面已经出去的人数等于,原来l前面的head

计算dpK:第一个人L在K位置的愤怒值,(原始开头head + 偏差(l - k))* a[l], new head= old head + (k - l)

计算dpR:右边第一个元素是k+1,最后一个是r, new head = old head + (k - l) + 1

注意head的计算


AC代码:

#include<stdio.h>#include<string>int a[101];int dp[101][101][101];int solve(int l , int r, int head) {if (l >  r) return 0;if (l == r) return a[l] * head;if (dp[l][r][head] != 0) return dp[l][r][head];int dpMin = INT_MAX;for (int k = l; k <= r; k++) {int dpL = solve(l + 1, k, head);int dpK = (head + k - l) * a[l];int dpR = solve(k + 1, r, head + k - l + 1);int sum = dpL + dpK + dpR;dpMin = dpMin < sum ? dpMin : sum;}return dp[l][r][head] = dpMin;}int main() {int ti, t;int i, n;scanf("%d", &t);for (ti = 0; ti < t; ti++) {memset(dp, 0, sizeof(dp));scanf("%d", &n);for (i = 0; i < n; i++) {scanf("%d", &a[i]);}int ans = solve(0, n - 1, 0);printf("Case #%d: %d\n", ti + 1, ans);}return 0;}


0 0
原创粉丝点击