HDU 2829 Lawrence
$dp$,斜率优化。
设$dp[i][j]$表示前$i$个数字切了$j$次的最小代价。$dp[i][j]=dp[k][j-1]+p[k+1][i]$。观察状态转移方程,可以发现是一列一列推导出来的。可以初始化第一列,然后算第二列,然后算第三列。
暴力算的话时间复杂度是$O(n^3)$,需要优化。将$p[x][y]$换成带有$sum$的式子,一顿化简后,发现可以斜率优化。具体过程与HDU 3507差不多。不再赘述。
#pragma comment(linker, "/STACK:1024000000,1024000000") #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<map> #include<set> #include<queue> #include<stack> #include<ctime> #include<iostream> using namespace std; typedef long long LL; const double pi=acos(-1.0),eps=1e-10; void File() { freopen("D:\\in.txt","r",stdin); freopen("D:\\out.txt","w",stdout); } template <class T> inline void read(T &x) { char c = getchar(); x = 0; while(!isdigit(c)) c = getchar(); while(isdigit(c)) { x = x * 10 + c - '0'; c = getchar(); } } int n,m; long long a[1005],sum[1005],p[1005][1005],dp[1005][1005]; int f1,f2,q[2000]; bool delete1(int x,int a,int b,int c) { if(dp[b][x]-p[1][b]+sum[b]*sum[b]-dp[a][x]+p[1][a]-sum[a]*sum[a]<sum[c]*(sum[b]-sum[a])) return 1; return 0; } bool delete2(int x,int a,int b,int c) { if((dp[c][x]-p[1][c]+sum[c]*sum[c]-dp[b][x]+p[1][b]-sum[b]*sum[b])*(sum[b]-sum[a])< (dp[b][x]-p[1][b]+sum[b]*sum[b]-dp[a][x]+p[1][a]-sum[a]*sum[a])*(sum[c]-sum[b])) return 1; return 0; } int main() { while(~scanf("%d%d",&n,&m)) { if(n==0&&m==0) break; m++; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); sum[i]=sum[i-1]+a[i]; } for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { int s=j,e=j+i-1; if(e>n) continue; if(i==1) { p[s][e]=0; continue; } p[s][e]=p[s+1][e]+a[s]*(sum[e]-sum[s]); } } for(int i=1;i<=n;i++) dp[i][1]=p[1][i]; for(int j=2;j<=m;j++) { f1=0; f2=0; q[f2]=j-1; for(int i=j;i<=n;i++) { while(1) { if(f2-f1+1<2) break; if(delete1(j-1,q[f1],q[f1+1],i)) f1++; else break; } dp[i][j] = dp[q[f1]][j-1]+p[q[f1]+1][i]; while(1) { if(f2-f1+1<2) break; if(delete2(j-1,q[f2-1],q[f2],i)) f2--; else break; } f2++; q[f2]=i; } } printf("%lld\n",dp[n][m]); } return 0; }