poj 3709

来源:互联网 发布:梁平知德小学怎么样 编辑:程序博客网 时间:2024/05/17 01:36

题意是给你一个n长度递增数列,将其分组,每组不少于m个,每组的cost是每组中所有元素减去里面最小元素的值的总和,要求你算最小的cost。我想说这类的分组问题一般都是dp解决的,这题也不例外,dp状态也很好设定,dp[i]表示前i个元素的cost最小值,当然是已经分好组了,因为没限定你要分几组就是一维的状态,但是把状态方程列出来后就会发现一个问题,状态转移方程dp[i]=dp[tem]+sum[i]-sum[tem]-(i-tem)*a[tem+1];m<tem<i;这个复杂度是n*mn<500000,m<n,显然超时,这时就要用到斜率+单调队列优化,我也是刚学,研究了好久才明白怎么做的,大意就是再队列中维护一个单调的斜率,详情不介绍了,可以baidu一下,很多资料,有了这个一下子就把复杂度降到了o(n),很快啊,二维的斜率优化就是用四边形不等式。这里需要说一个细节if(!(G(x,y)*S(y,z)<G(y,z)*S(x,y)))这个条件是保证斜率单调,之前sb的写了好多条件都wa了。还有中间会溢出,要用longlong啊。

效率还可以,不知道那些200+ms怎么做的

Run IDUserProblemResultMemoryTimeLanguageCode LengthSubmit Time91556822010307204253709Accepted15832K1235MSC++1139B2011-08-14 00:31:3191553952010307204253709Accepted16368K1219MSG++1244B2011-08-13 23:16:16

#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<string>#include<cmath>using namespace std;long long  dp[500005],sum[500005],a[500005];long long G(long long x,long long y){return dp[y]+sum[x]+y*a[y+1]-sum[y]-dp[x]-x*a[x+1];}long long S(long long x,long long y){return a[y+1]-a[x+1];}long long q[500005];int r,f;void insert(int k,int t){q[++r]=k;for(long long i=r-1;i>f+1;i--){long long x=q[i-1],y=q[i],z=q[i+1];if(!(G(x,y)*S(y,z)<G(y,z)*S(x,y)))q[i]=q[r--];elsebreak;}}void init(){r=f=0;memset(q,0,sizeof(q));memset(dp,0,sizeof(dp));}int main(){int c,n,m;scanf("%d",&c);while(c--){scanf("%d%d",&n,&m);sum[0]=0;init();for(int i=1;i<=n;i++){scanf("%lld",&a[i]);sum[i]=sum[i-1]+a[i];}for(long long i=1;i<=n;i++){while(f+1<r&&G(q[f+1],q[f+2])<=i*S(q[f+1],q[f+2]))f++;long long tem=f<r?q[f+1]:0;dp[i]=dp[tem]+sum[i]-sum[tem]-(i-tem)*a[tem+1];if(i>=2*m-1){insert(i-m+1,i+1);}}printf("%lld\n",dp[n]);}return 0;}



原创粉丝点击