HDU-4283 You Are the One 区间DP
该题题意为给定一个序列,这个序列能够按照栈的规则进行进出,只不过每个位置的数具有一个数值,每次用出来的顺序减1乘以这个数值,求最后的综合最小。
我们有这样的设想,对于一个序列 A B C ... Z,他们有相对应的数值 value[A], value[B] ... value[Z],现在对整个串进行分析,我们试图分析能否找到该问题的子问题,设f[i][j] 为 i 到 j 这个序列能够出来的最小值,那么对于第i号元素来说,我们有如下选择,先出去若干个元素再让a出去,对于某一个区间,我们又可以细分下去。为什么要选择 i 号元素来考虑,因为我们考虑 i 号元素时,选择其他的元素段来考虑都将是一个连续的段落,这样就可以递归下去处理。
还有一个问题就是当每次计算[a,b]时,我们都是单独的考虑这一段,也就是说如果a,a+1号元素出队的话,那么数值统计就是 (1-1) * value[a] + (2-1) * value[b]; 对于任意一段递归下去的区间都是这样计算,那么这样可能又会有疑问了,如何保持题中所说的顺序了,技巧就是当[a, b]前有 1,2...a-1 元素已经出队了,那么整个数值要加上 sum(value[a...b]) * (a-1)。可以去计算一下。
代码如下:
#include <iostream> #include <cstdlib> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <string> #include <map> #include <queue> #include <vector> #include <stack> #include <list> #include <set> #define INF 0x3f3f3f3f #define MAXN 100 using namespace std; int N, s[MAXN+5], f[MAXN+5][MAXN+5], seq[MAXN+5]; int dfs(int x, int y) { if (f[x][y] != INF) { return f[x][y]; } else if (x >= y) return 0; for (int i = 0; i <= y-x ; ++i) { f[x][y] = min(f[x][y], dfs(x+1, x+i) + dfs(x+i+1, y) + (i+1)*(s[y]-s[x+i]) + i*seq[x]); } return f[x][y]; } int main() { int T, ca = 0; scanf("%d", &T); while (T--) { memset(f, 0x3f, sizeof (f)); scanf("%d", &N); for (int i = 1; i <= N; ++i) { scanf("%d", seq+i); s[i] = s[i-1] + seq[i]; } printf("Case #%d: %d\n", ++ca, dfs(1, N)); } return 0; }