题目提交点
反思
题目看不懂,以后看题目要把他所说的仔仔细细看个三遍,将重要信息提取出来。
看了网上题解才知道这个题目是什么意思。
题解
- 题目分析
使用栈来调整原始的出场顺序让不开心值总值最小(原始的出场顺序就是依次入栈的顺序)。
- 类型分析
栈调整过后的顺序有很多种 + 求最小值,往DP思路上思考。
我们先分析一个例子,加入一个入栈顺序为1 2 3 4 5
。
如果我们让1第3个出场, 那么有一种可能情况为:3 2 1 4 5
,区间
[
2
,
3
]
[2, 3]
[2,3]是在1的前面,区间
[
4
,
5
]
[4, 5]
[4,5]是在1的后面,并且这个区间中的顺序是可以变化的。
通过这个例子我们就可以发现:我们可以将一个区间划分成两个相互独立的区间。正好与区间DP使用条件类似,所以尝试使用区间DP做这个题目。
- DP分析
我们状态定义为两维,
f
(
l
,
r
)
f(l, r)
f(l,r)集合表示为区间
[
l
,
r
]
[l, r]
[l,r]中所有人出栈不开心值的总和。
状态转移方程:
f
(
l
,
r
)
=
m
i
n
(
f
(
l
,
r
)
,
f
(
l
+
1
,
l
+
k
−
1
)
+
f
(
l
+
k
,
r
)
+
w
i
∗
(
k
−
1
)
+
k
∗
(
s
r
−
s
l
+
k
−
1
)
)
f(l, r) = min(f(l, r), f(l + 1, l + k - 1) + f(l + k, r) + w_i * (k - 1) + k * (s_r - s_{l + k - 1}))
f(l,r)=min(f(l,r),f(l+1,l+k−1)+f(l+k,r)+wi∗(k−1)+k∗(sr−sl+k−1))
k
k
k表示在区间
[
l
,
r
]
[l,r]
[l,r]中第k个出栈,这个k是在这个区间中的相对位置,所以的话,如果我们要将子区间组合成总区间的话,还需要加一个偏移量:
k
∗
(
s
r
−
s
l
+
k
−
1
)
)
k * (s_r - s_{l + k - 1}))
k∗(sr−sl+k−1)), 区间
[
l
+
1
,
l
+
k
−
1
]
[l + 1, l + k - 1]
[l+1,l+k−1]是在总区间的前面,所以不用加,而区间
[
l
+
k
,
r
]
[l + k, r]
[l+k,r]是在总区间的后面,所以他们每一个人都会多等k个人:
w
l
+
k
∗
k
+
w
l
+
k
+
1
∗
k
+
w
l
+
k
+
2
∗
k
+
w
l
+
k
+
3
∗
k
+
…
…
w
r
∗
k
w_{l + k} * k + w_{l + k + 1} * k + w_{l + k + 2} * k + w_{l + k + 3} * k + …… w_{r} * k
wl+k∗k+wl+k+1∗k+wl+k+2∗k+wl+k+3∗k+……wr∗k
AC 代码
#include<bits/stdc++.h>
using namespace std;
#define _for(i, a, b) for (int i = (a); i <= (b); ++i)
#define _rep(i, a, b) for (int i = (a); i >= (b); --i)
#define debug(a) cout << #a << " = " << a << endl
#define mod(x) (x) % MOD
#define ENDL "\n"
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;
const int N = 100 + 7, MOD = 1e9, INF = 0x3f3f3f3f;
int f[N][N], w[N], s[N];
int main()
{
#ifdef LOCAL
freopen("data.in", "r", stdin);
#endif
ios::sync_with_stdio(false);
cout.tie(0), cin.tie(0);
int T;
cin >> T;
_for(kase, 1, T){
int n;
cin >> n;
_for(i, 1, n) {
cin >> w[i];
s[i] = w[i] + s[i - 1];
}
_for(len, 1, n) _for(l, 1, n - len + 1) {
int r = l + len -1;
f[l][r] = INF;
_for(k, 1, r - l + 1)
f[l][r] = min(f[l][r], f[l + 1][l + k - 1] + f[l + k][r] + w[l] * (k - 1) + k * (s[r] - s[l + k - 1]));
}
printf("Case #%d: %d\n", kase, f[1][n]);
}
return 0;
}