题意是给你一个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 ID | User | Problem | Result | Memory | Time | Language | Code Length | Submit Time |
9155682 | 201030720425 | 3709 | Accepted | 15832K | 1235MS | C++ | 1139B | 2011-08-14 00:31:31 |
9155395 | 201030720425 | 3709 | Accepted | 16368K | 1219MS | G++ | 1244B | 2011-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--]; else break; } } 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; }