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.
5 5 5 9 5 7 5
230
题意:给出一行数,按从左到右的顺序将这段数分块,每一块的代价为区间和的平方+常数M。求最小代价。
思路:用dp[i]表示到i的最小代价,sum[i]表示前缀和
n^2算法是很好想的,显然是超时的...
假设k<j,考虑从k转移到i和从j转移到i这两种决策
1.dp[i] = dp[k]+(sum[i]-sum[k])^2+M
2.dp[i] = dp[j]+(sum[i]-sum[j])^2+M
如果决策j比k更优,则
dp[j]+sum[i]^2-2*sum[i]*sum[j]+sum[j]^2+M < dp[k]+sum[i]^2-2*sum[i]*sum[k]+sum[k]^2+M
化简一下可得(dp[j]+sum[j]^2-dp[k]-sum[k]^2)/(2*(sum[j]-sum[k]) < sum[i]
令yj = dp[j] + sum[j]^2,xj = 2*sum[j];
可以看出左边是个斜率的表达式,我们这里用g[k,j]表示
于是g[k,j] < sum[i]即选择j比选择k的决策更优.
假如g[k,j] >= g[j,i],那么j永远不会被选择,理由如下:
1.假设g[k,j] < sum[i],那么j比k更优,但是i比j更优
2.假设g[k,j] >= sum[i],那么k比j更优
于是我们想到了一个显然的优化,维护一个斜率上升的栈。每次进来一个新点c之前,从栈顶拿2个元素a,b。如果g[a,b] >= g[b,c]。
那么就把b这个点去掉,循环直到斜率满足g[a,b] < g[b,c].
那...dp[i]怎么求呢...
显然把队列里遍历一遍是肯定能找到解的=_=
但是我们仔细看不等式右边的c[i].在题意中默认是>=0.
假设k<j<i.现在我们假设j比k更优.也就是dp[i] = dp[j]+(sum[i]-sum[j])^2+M.
那么对于一个x>i.dp[x]不可能选择j之间的决策,比如k,为什么呢?
因为sum[x] >= sum[i].如果k的决策比j要更优,则有g[k,j]>sum[x].但这与g[k,j]<=sum[i]不符.
到这里就说得差不多了...如果这题的c[i]可以为负...貌似就必须每次都遍历整个栈了?
感谢http://www.cnblogs.com/ka200812/archive/2012/08/03/2621345.html
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define maxn 500080 #define inf 0x3f3f3f3f int a[maxn],sum[maxn],dp[maxn]; int q[maxn],M; bool Judge(int a,int b,int Sum) { if(dp[b]+sum[b]*sum[b]-dp[a]-sum[a]*sum[a] < Sum*(2*sum[b]-2*sum[a])) return 1; return 0; } bool Judge1(int a,int b,int c) { if((dp[b]+sum[b]*sum[b]-dp[a]-sum[a]*sum[a])*(2*(sum[c]-sum[b])) >= ((dp[c]+sum[c]*sum[c]-dp[b]-sum[b]*sum[b])*(2*(sum[b]-sum[a])))) return 1; return 0; } int GetAns(int j,int i) { return (dp[j] + (sum[i]-sum[j])*(sum[i]-sum[j]) + M); } inline int min(int a,int b,int c) { if(a>b) a = b; if(a>c) a = c; return a; } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int n; while(scanf("%d%d",&n,&M)==2) { for(int i = 1;i <= n;i++) scanf("%d",&a[i]); sum[0] = dp[0] = 0; for(int i = 1;i <= n;i++) sum[i] = sum[i-1] + a[i]; int first = 0,rear = 0; q[rear++] = 0; for(int i = 1;i <= n;i++) { //先确定dp[i] while(first+1 < rear && Judge(q[first],q[first+1],sum[i])) first++; dp[i] = GetAns(q[first],i); while(first+1 < rear && Judge1(q[rear-2],q[rear-1],i)) rear--; q[rear++] = i; } printf("%d\n",dp[n]); } }