题意: 有一列人, 他们每个人有屌丝值, 每个人根据上场顺序会有不满意度, 是在他之前上场人数乘屌丝值. 现在有一个栈可以暂时人, 问最后所有人都上场最少会产生多少不满意度.
思路:
dp[i][j]表示从第i到第j个人最少产生的不满意度.
现在假设i到j已经是最优区间, 考虑i前面的人i-1:
这个人可能Ⅰ直接上场, 或者Ⅱ进栈之后在j之后上场.
那么dp[i-1][j]就会由两个值进行转移: Ⅰdp[i-1][i-1]+dp[i][j] 和 Ⅱdp[i][j]-①sum(a from i to j)+②a[i-1]*j.
Ⅰ是显然的, 主要说说Ⅱ:
因为dp[i[[j]已经是最优的区间, 我们如果不动它, 那么如果i-1这个人如果等着的话, 就必须在j之后上场. 那么i到j的人会减少①的不满意度, i-1这个人的不满意度就是②.
以上区间扩展, 当然还有区间合并, 枚举每个位置, 当前区间由两个由k分隔的区间简单相加组成.
其实Ⅰ也是区间合并的一种, 所以我在代码中并没有写Ⅰ, 只是让k在区间合并里从s开始枚举, 与Ⅰ的操作是等价的.
代码:
#include<bits/stdc++.h>
using namespace std;
void debug_out() {
cerr << '\n';
}
template<typename T, typename ...R>
void debug_out(const T &f, const R &...r) {
cerr << f << " ";
debug_out(r...);
}
#define debug(...) cerr << "[" << #__VA_ARGS__ << "]: ", debug_out(__VA_ARGS__);
typedef long long ll;
const int M = 105;
const int inf = 1e9 + 5;
const int mod = 1e9 + 7;
int _;
int n;
int a[M];
int dp[M][M];
void init() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); }
fill(dp[0], dp[0] + M * M, inf);
for (int len = 1; len <= n; len++) {
for (int s = 1; s + len - 1 <= n; ++s) {
int t = s + len - 1;
if (len == 1) {
dp[s][t] = (s - 1) * a[s];
continue;
}
int sum = 0;
for (int i = s + 1; i <= t; i++)sum += a[i];
dp[s][t] = dp[s + 1][t] + a[s] * (t - 1) - sum;//这里是Ⅱ
//以上区间扩展
for (int k = s; k < t; ++k) {//区间合并(包括Ⅰ)
dp[s][t] = min(dp[s][t], dp[s][k] + dp[k + 1][t]);
}
}
}
/* for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
printf("[%d,%d]:%d ", i, j, dp[i][j]);
}
puts("");
}*/
printf("%d\n", dp[1][n]);
}
void solve() {
}
int main() {
int caz = 0;
scanf("%d", &_);
while (_--) {
printf("Case #%d: ", ++caz);
init();
solve();
}
return 0;
}