HDU3507 Print Article —— 斜率优化DP
題目鏈接:https://vjudge.net/problem/HDU-3507
?
Print Article
Time Limit: 9000/3000 MS (Java/Others)????Memory Limit: 131072/65536 K (Java/Others)
Total Submission(s): 14899????Accepted Submission(s): 4648
One day Zero want to print an article which has N words, and each word i has a cost Ci to be printed. Also, Zero know that print k words in one line will cost
M is a const number.
Now Zero want to know the minimum cost in order to arrange the article perfectly.
?
Input There are many test cases. For each test case, There are two numbers N and M in the first line (0 ≤ n ≤?500000, 0 ≤ M ≤ 1000). Then, there are N numbers in the next 2 to N + 1 lines. Input are terminated by EOF.?
Output A single number, meaning the mininum cost to print the article.?
Sample Input 5 5 5 9 5 7 5?
Sample Output 230?
Author Xnozero?
Source 2010 ACM-ICPC Multi-University Training Contest(7)——Host by HIT?
Recommend zhengfeng?
?
題意:
給出一段字符串,每個位置上的字符都有其相應的價值Ci。將字符串分成若干子串,且每個子串的價值為sigma(Ci)^2+M,i的范圍為區間的范圍。問怎樣分割能得到最小的價值?
?
題解:
動態規劃問題,設dp[i]為前i個字符的最小價值。再設sum[i]為前i個字符的價值前綴和。
可得:dp[i] = min( dp[j] + (sum[i]-sum[j])^2 + M ) ) , 其中 0<=k<=i-1。
整理:dp[i] = min( dp[j] + sum[i]^2 + sum[j]^2 - 2*sum[i]*sum[j] + M ), 其中 0<=j<=i-1。
最直接的做法是枚舉j,求得最小值。但是此題n的范圍為5e5,O(n^2)肯定超時了,所以要借用斜率優化,其方法是盡量排除掉那些不可能取得最優值的點,縮小狀態轉移的范圍。推理如下:
?
1.如果 k<j,假設dp[i]在j處取得的值比k處取得的值要小,即更優,那么就有:
dp[j] + sum[i]^2 + sum[j]^2 - 2*sum[i]*sum[j] + M <?dp[k] + sum[i]^2 + sum[k]^2 - 2*sum[i]*sum[k] + M,
整理得:[ (dp[j] + sum[j]^2) - (dp[k] + sum[k]^2) ] / ( 2*sum[j] - 2*sum[k] ) < sum[i]。
觀察等式右邊,可以看出這是一個斜率表達式。
我們設 yj =?dp[j] + sum[j]^2, xj =?2*sum[j] ,那么上式就變為:( yj - yk ) / ( xj - xk ) < sum[i] 。
可知 ( yj - yk ) / ( xj - xk ) 就是直線 j---k 的斜率g[j,k]。
所以:當k<j,且j比k更優時, g[j,k] < sum[i]。而且,因為sum[i]遞增,所以j比k更優的結論,對于i以后的位置也合適。……結論1(此結論用于求出dp[i]的最優轉移狀態)
?
2.當k<j<i時, 如果g[i,j] <= g[j,k]時, j可以直接排除。 ………………結論2(此結論用于維護隊列)
1) 當g[i,j] < sum[i]時, i比j更優, 所以排除j。
2) 當g[i,j] >= sum[i] 時, g[j, k] >= sum[i],表明k比j更優,所以排除j。
?
3.綜上:只需維護一個隊列,其兩個相鄰元素間所形成直線的斜率單調遞增。
?
注意:
判斷不等式的時候,由于避免整除除法的問題,把除法判斷改成了乘法判斷,但是要特別注意,移項是否為負數,如果為負數,那么不等式的方向就會發生變化。
?
?
代碼如下:
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 MAXM = 1e5+10; 18 const int MAXN = 5e5+10; 19 20 int dp[MAXN], sum[MAXN]; 21 int q[MAXN]; 22 int n, M; 23 24 int getUp(int i, int j) 25 { 26 return (dp[i]+sum[i]*sum[i]) - (dp[j]+sum[j]*sum[j]); 27 } 28 29 int getDown(int i, int j) 30 { 31 return 2*(sum[i]-sum[j]); 32 } 33 34 int getDp(int i, int j) 35 { 36 return dp[i] = dp[j] + (sum[i]-sum[j])*(sum[i]-sum[j]) + M; 37 } 38 39 int main() 40 { 41 while(scanf("%d%d", &n, &M)!=EOF) 42 { 43 sum[0] = 0; 44 for(int i = 1; i<=n; i++) 45 scanf("%d", &sum[i]), sum[i] += sum[i-1]; 46 47 dp[0] = 0; 48 int head = 0, tail = 0; 49 q[tail++] = 0; 50 for(int i = 1; i<=n; i++) 51 { 52 //以下為尋找最優的轉移狀態。由于除法改成了乘法,所以順序不能任意,否則不等號方向會改變。 53 while(head+1<tail && getUp(q[head+1], q[head])<sum[i]*getDown(q[head+1], q[head])) head++; 54 dp[i] = getDp(i, q[head]); 55 //以下為維護隊列 56 while(head+1<tail && getUp(i, q[tail-1])*getDown(q[tail-1], q[tail-2])<= 57 getUp(q[tail-1], q[tail-2])*getDown(i, q[tail-1]) ) tail--; 58 q[tail++] = i; 59 } 60 61 printf("%d\n", dp[n]); 62 } 63 } View Code?
轉載于:https://www.cnblogs.com/DOLFAMINGO/p/8177150.html
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的HDU3507 Print Article —— 斜率优化DP的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MacOS系统升级后,IDEA的SVN不
- 下一篇: spring boot: 计划任务@ E