HDOJ 4283 You Are the One(经典区间dp)
题意:
给定一个序列,序列内的人有屌丝值Di,第i个人如果是第k个出场,那么他的屌丝值为Di * (k-1), 但是导演可以通过一个栈来调整序列里面人的出场顺序。
求一个出场序列使总屌丝值最小。
思路:
题意一开始有点儿混乱,但是最后要明白一点:导演对于这个出场顺序的影响只是一定程度上的。比如说:
1. 第一个人第k个出场
2. 那么要求2~k的人都要在第一个人前面出场
3. k+1~n的人都要在k以后出场
明白了上面的过程,就可以定义区间dp[i, j]表示区间[i, j]在相对于i为起点情况下i在第k个出场的最小屌丝总值。
1. dp[i, j] = (k - i) * Di + dp[i+1, k]
2. dp[i, j] += (k + 1 - i) * (sum[j] - sum[k]) + dp[k+1, j]
注意一切都是相对的,直到dp[1, n]的时候就是相对于1了,dp[1, n]就是所要求的值。
#include <cstdio> #include <cstdlib> #include <cstring> #include <climits> #include <algorithm> using namespace std; const int MAXN = 110; int a[MAXN], sum[MAXN]; int dp[MAXN][MAXN]; int solve(int n) { for (int i = 1; i <= n; ++i) dp[i][i] = dp[i+1][i] = 0; for (int p = 2; p <= n; ++p) for (int i = 1, j = p; j <= n; ++i, ++j) { dp[i][j] = INT_MAX; for (int k = i; k <= j; ++k) dp[i][j] = min(dp[i+1][k] + dp[k+1][j] + (k-i)*a[i] + (k-i+1)*(sum[j]-sum[k]), dp[i][j]); } return dp[1][n]; } int main() { int cases, t = 0; char b[20]; scanf("%d", &cases); while (cases--) { int n; scanf("%d", &n); sum[0] = 0; for (int i = 1; i <= n; ++i) scanf("%d", &a[i]), sum[i] = a[i] + sum[i-1]; printf("Case #%d: %d\n", ++t, solve(n)); } return 0; }
-------------------------------------------------------
kedebug
Department of Computer Science and Engineering,
Shanghai Jiao Tong University
E-mail: kedebug0@gmail.com
GitHub: http://github.com/kedebug
-------------------------------------------------------