4 1 4 5 1 2 4 2 4 5 1 2 0 0
172
这题要先初始化cost[i][j],表示从i到j累加的总分,然后用dp[i][j]表示前i个数炸j次最后得到的最小值,要用四边形优化。
#include<iostream> #include<stdio.h> #include<stdlib.h> #include<string.h> #include<math.h> #include<vector> #include<map> #include<set> #include<queue> #include<stack> #include<string> #include<algorithm> using namespace std; #define ll long long ll dp[1005][1006],a[1006],cost[1006][1006]; int s[1006][1006]; ll min(ll a,ll b){ return a<b?a:b; } int main() { int n,m,i,j,k; ll sum,maxn; while(scanf("%d%d",&n,&m)!=EOF) { if(n==0 && m==0)break; for(i=1;i<=n;i++){ scanf("%lld",&a[i]); } for(i=1;i<=n;i++){ sum=a[i]; cost[i][i]=0; for(j=i+1;j<=n;j++){ cost[i][j]=cost[i][j-1]+sum*a[j]; sum+=a[j]; } } maxn=cost[1][n]+10; for(i=2;i<=n;i++){ dp[i][1]=maxn; for(k=1;k<i;k++){ dp[i][1]=min(dp[i][1],cost[1][k]+cost[k+1][i]); } s[i][1]=2; } /*printf("--->%lld\n",dp[n][1]);*/ for(j=2;j<=m;j++){ s[n+1][j]=n-1; for(i=n;i>j;i--){ dp[i][j]=maxn; for(k=s[i][j-1];k<=s[i+1][j];k++){ if(dp[i][j]>dp[k][j-1]+cost[k+1][i]){ dp[i][j]=dp[k][j-1]+cost[k+1][i]; s[i][j]=k; } } /*for(k=j;k<i;k++){ dp[i][j]=min(dp[i][j],dp[k][j-1]+cost[k+1][i]); }*/ } /*printf("--->%d %lld\n",j,dp[n][j]);*/ } printf("%lld\n",dp[n][m]); } return 0; }
这题也可以用斜率优化做,dp[i][j]=min{dp[k][j-1]+cost[k+1][i]},cost[i][j]表示i到j的总花费,那么cost[1][i]=cost[1][k]+cost[k+1][i]+sum[k]*(sum[i]-sum[k]),那么dp[i][j]=dp[k][j-1]+cost[1][i]-cost[1][k]-sum[k]*(sum[i]-sum[k]),那么可以设y=dp[k][j-1]-cost[1][k]+sum[k]*sum[k],x=sum[k],斜率为sum[i].并设g(i,j)=(yj-yi)/(xj-xi).当g(k1,k2)<=sum[i]时,说明k2比k1优,可以删除k2,如果g(k1,k2)>=g(k2,i)时,可以删去k2,由这两个来维护上凸的图形。
#include<iostream> #include<stdio.h> #include<stdlib.h> #include<string.h> #include<vector> #include<map> #include<set> #include<queue> #include<stack> #include<string> #include<algorithm> using namespace std; #define ll long long #define inf 999999999 #define maxn 1006 ll cost[maxn],dp[maxn][maxn],sum[maxn],a[maxn],q[1111111]; int i,j,n,m; ll getup(int k){ return dp[k][j-1]-cost[k]+sum[k]*sum[k]; } ll getdown(int k){ return sum[k]; } int main() { int front,rear; ll k; while(scanf("%d%d",&n,&m)!=EOF) { if(n==0 && m==0)break; sum[0]=cost[0]=0; for(i=1;i<=n;i++){ scanf("%lld",&a[i]); sum[i]=sum[i-1]+a[i]; cost[i]=cost[i-1]+sum[i-1]*a[i]; } for(i=1;i<=n;i++){ dp[i][0]=cost[i]; } for(j=1;j<=m;j++){ front=rear=0; q[rear]=j; for(i=j+1;i<=n;i++){ while(front<rear && getup(q[front+1])-getup(q[front])<=sum[i]*( getdown(q[front+1])-getdown(q[front]) ) ){ front++; } k=q[front]; dp[i][j]=dp[k][j-1]+cost[i]-cost[k]-sum[k]*(sum[i]-sum[k]); while(front<rear && ( getup(q[rear])-getup(q[rear-1]) )*(getdown(i)-getdown(q[rear]) )>=( getup(i)-getup(q[rear]) )*(getdown(q[rear])-getdown(q[rear-1]) ) ){ rear--; } rear++; q[rear]=i; } } printf("%lld\n",dp[n][m]); } return 0; }