码迷,mamicode.com
首页 > 其他好文 > 详细

[HDU 3507]Print Article

时间:2017-09-22 22:30:02      阅读:99      评论:0      收藏:0      [点我收藏+]

标签:复杂   for   i++   题目   names   limit   代码   acm   code   

题目大意:

将一串数字分成许多子串,输出每串的代价是子串数和的平方加常数M。求代价最小值。

 

首先DP是毫无疑问的,但是直接搞药丸,时间复杂度过高,那该怎么优化呢?

这时候,就该使用斜率优化了。

不多说,先列出DP方程:\(d{p_i} = \mathop {\min }\limits_{j < i} (d{p_j} + M + {(su{m_i} - su{m_j})^2})\)

我们设有k使\(k < j < i\),若决策j比决策k更优,则\(d{p_j} + M + {(su{m_i} - su{m_j})^2} < d{p_k} + M + {(su{m_i} - su{m_k})^2}\)

简化后得\(\frac{{(d{p_j} + sum_j^2) - (d{p_k} + sum_k^2)}}{{2 \times (su{m_j} - su{m_k})}} < su{m_i}\)

维护单调序列即可。

具体看代码:

 

 1 #include <cstdio>
 2 using namespace std;
 3 const int N = 500050;
 4 int n,m,head,tail;
 5 int sum[N],dp[N],q[N];
 6 int gdp(int i,int j){return (dp[j] + m + (sum[i] - sum[j]) * (sum[i] - sum[j]));}
 7 int gup(int j,int k){return ((dp[j] + sum[j] * sum[j]) - (dp[k] + sum[k] * sum[k]));}
 8 int gdown(int j,int k){return 2 * (sum[j] - sum[k]);}
 9 int main()
10 {
11     while(~scanf("%d %d",&n,&m))
12     {
13         sum[0] = dp[0] = 0;
14         for(int i = 1;i <= n;i++){scanf("%d",&sum[i]);sum[i] += sum[i - 1];}
15         head = tail = 0;q[tail++] = 0;
16         for(int i = 1;i <= n;i++)
17         {
18             while(head + 1 < tail && gup(q[head + 1],q[head]) <= sum[i] * gdown(q[head + 1],q[head])) head++;
19             dp[i] = gdp(i,q[head]);
20             while(head + 1 < tail && gup(i,q[tail - 1]) * gdown(q[tail - 1],q[tail - 2]) <= gup(q[tail - 1],q[tail - 2]) * gdown(i,q[tail - 1])) tail--;
21             q[tail++] = i;
22         }
23         printf("%d\n",dp[n]);
24     }

讲的似乎不大详细,下次有时间详细写写。

[HDU 3507]Print Article

标签:复杂   for   i++   题目   names   limit   代码   acm   code   

原文地址:http://www.cnblogs.com/gcc314/p/7577095.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!