【hdu4283】区间动态规划问题

题目编号:

HDU4283


题目链接:

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


题目描述:

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

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

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

【hdu4283】区间动态规划问题_第1张图片

样例输入:

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】

如图:

【hdu4283】区间动态规划问题_第2张图片


代码中核心: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
#include

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;
}


你可能感兴趣的:(动态规划,HDU)