标签:复杂 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 }
讲的似乎不大详细,下次有时间详细写写。
标签:复杂 for i++ 题目 names limit 代码 acm code
原文地址:http://www.cnblogs.com/gcc314/p/7577095.html