HDU4283 You Are the One —— 区间DP
题目链接:https://vjudge.net/problem/HDU-4283
You Are the One
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 4667 Accepted Submission(s): 2218
The next n line are n integer D1-Dn means the value of diaosi of boys (0 <= Di <= 100)
题意:
有n个人站成一排准备上台表演,由于每个人都想是第一个站到台上,所以如果需要等待就会不开心。每个人都有一个值a[i],表示每等待一个人,不开心度就会+a[i]。工作人员为了使得所有人的不开心总值最小,就设置了一个小黑屋(一个栈),可以把队列的人先安排到小黑屋里,以此调节出场顺序。
题解:
1.可知:在一个区间 [l, r] 内, 如果l是第 k (1<=k<=r-l+1)个出场的,那么 [l+1, l+k-1] 必定是先与l出场的(根据栈的性质,如果要得到栈底元素,那么必须把栈底元素上面的所有元素出栈), 所以剩下的 [l+k, r] 是在l之后出场的。
2.根据第一条结论,如果确定了l在区间 [l, r] 是第k个出场的(不需要考虑区间之外的人),那么就可以把区间分为 [l+1, l+k-1] 和 [l+k, r],且这两个区间是互不影响的,所以又可以根据上述结论分别对这两个区间单独求值。
3.那怎么计算区间的不开心总值呢?
由于我们知道l在当前区间内是第k个出场的,所以我们可以先得到 a[l]*(k-1)。对于区间[l+1, l+k-1]的人,由于他们是先于l出场的,所以对于这个总体来说,他们是没有延迟的,所以直接加上dp[l+1, l+k-1]。对于区间[l+k, r]的人,由于他们是在l之后出场的,而l在这个区间内又是第k个出场的,所以对于 [l+k, r]这个总体来说,他们是整体延迟了k个人的,所以先加上 k*(sum[r]-sum[l+k-1]),加上了延迟所带来的不开心值之后,我们就可以把 区间[l+k, r]的出场是没有延迟的,所以情况跟区间[l+1, l+k-1]一样,再加上dp[l+k, r]就行了。
学习之处:
1.栈的性质: 如果要得到栈底元素,那么必须把栈底元素上面的所有元素出栈(这个结论虽然显而易见,但是由这个结论再推出另外的结论却并非易事)。
2.求值除了一步求得之外,还可以分步求值,一个阶段只求一部分。
记忆化搜索:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 #include <cmath> 7 #include <queue> 8 #include <stack> 9 #include <map> 10 #include <string> 11 #include <set> 12 using namespace std; 13 typedef long long LL; 14 const int INF = 2e9; 15 const LL LNF = 9e18; 16 const int MOD = 1e9+7; 17 const int MAXN = 100+10; 18 19 int a[MAXN]; 20 int sum[MAXN], dp[MAXN][MAXN]; 21 22 int dfs(int l, int r) 23 { 24 if(l>=r) return 0; 25 if(dp[l][r]!=-1) return dp[l][r]; 26 27 dp[l][r] = INF; 28 for(int k = 1; k<=r-l+1; k++) 29 { 30 int tmp = dfs(l+1, l+k-1)+dfs(l+k,r)+(k-1)*a[l]+k*(sum[r]-sum[l+k-1]); 31 dp[l][r] = min( dp[l][r], tmp ); 32 } 33 return dp[l][r]; 34 } 35 36 int main() 37 { 38 int T, n, kase = 0; 39 scanf("%d", &T); 40 while(T--) 41 { 42 scanf("%d", &n); 43 sum[0] = 0; 44 for(int i = 1; i<=n; i++) 45 scanf("%d", &a[i]), sum[i] = sum[i-1]+a[i]; 46 47 memset(dp, -1, sizeof(dp)); 48 dfs(1, n); 49 printf("Case #%d: %d\n", ++kase, dp[1][n]); 50 } 51 }
递推:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 #include <cmath> 7 #include <queue> 8 #include <stack> 9 #include <map> 10 #include <string> 11 #include <set> 12 using namespace std; 13 typedef long long LL; 14 const int INF = 2e9; 15 const LL LNF = 9e18; 16 const int MOD = 1e9+7; 17 const int MAXN = 100+10; 18 19 int a[MAXN]; 20 int sum[MAXN], dp[MAXN][MAXN]; 21 22 int main() 23 { 24 int T, n, kase = 0; 25 scanf("%d", &T); 26 while(T--) 27 { 28 scanf("%d", &n); 29 sum[0] = 0; 30 for(int i = 1; i<=n; i++) 31 scanf("%d", &a[i]), sum[i] = sum[i-1]+a[i]; 32 33 memset(dp, 0, sizeof(dp)); 34 for(int len = 2; len<=n; len++) 35 { 36 for(int l = 1; l<=n-len+1; l++) 37 { 38 int r = l+len-1; 39 dp[l][r] = INF; 40 for(int k = 1; k<=r-l+1; k++) 41 { 42 int tmp = dp[l+1][l+k-1] + dp[l+k][r] + (k-1)*a[l] + k*(sum[r]-sum[l+k-1]); 43 dp[l][r] = min(dp[l][r], tmp); 44 } 45 } 46 } 47 printf("Case #%d: %d\n", ++kase, dp[1][n]); 48 } 49 }