HDU - 4283 You Are the One 区间dp
题目链接
题目大意:有n个人排成一排要上台表演,每个人有一个屌丝值pi。第i个上台表演的人,他的不满意度为(i-1)*pi。
现在有一个类似于栈的黑屋子,你可以让某些人进入这个黑屋子。这些人要按照排的顺序来,那么对于排在最前面的人,
就有两个选择:
(1)让他直接上台表演;
(2)让他暂时进黑屋子。
现在请你选择一个合理的调度顺序,使得最后的总不满意度最小?
题目分析:
这个题啊 把我学区间dp的热情都浇灭了。。。
dp[i][j] 表示i~j这个区间里的最小不满意度。对于这个区间里面的第一个元素i,他有可能第一个上台,也有可能第2个第j - i + 1个上台。所以我们的第三重for循环来枚举他的上场顺序。
假设他第k个上场,那么第 i + 1 ~ i + k - 1,肯定在他之前,这时的花费就是dp[i + 1][i + k - 1]。第 i + k ~ j在他之后,而dp[i + k][j]表示的是他们里面的顺序从1开始的不满意值,现在原来第一个上场的变成了第k + 1个,所以要在原来的基础上加上(sum[j] - sum[k]) * (k - i + 1)。
其实我觉得到这里还是很好理解的,不好理解的是为什么这样做就是对的。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 100;
int dp[maxn][maxn], data[maxn], sum[maxn];
int main()
{
int t, cas = 1;
scanf("%d", &t);
while(t--)
{
int n;
scanf("%d", &n);
memset(dp, 0, sizeof(dp));
for(int i = 1; i <= n; i++) {
scanf("%d", &data[i]);
sum[i] = sum[i - 1] + data[i];
}
for(int i = 1; i<= n; i++)
for(int j = i + 1; j <= n; j++)
dp[i][j] = inf;
for(int len = 2; len <= n; len++) {
for(int i = 1; i <= n; i++) {
int j = i + len - 1;
if(j > n) break;
for(int k = i; k <= j; k++) {
dp[i][j] = min(dp[i][j], (k - i) * data[i] + dp[i + 1][k] + (sum[j] - sum[k]) * (k - i + 1) + dp[k + 1][j]);
}
}
}
printf("Case #%d: %d\n", cas++, dp[1][n]);
}
}