题意:N个人排成一行,分成K组,要求每组的不和谐值之和最小。
思路:开始以为是斜率优化DP,但是每个区间的值其实已经知道了,即是没有和下标有关的未知数了,所以没必要用斜率。 四边形优化。
dp[i][j]表示前j个人分为i组的最小代价。 622ms
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=4010; int sum[maxn][maxn],cost[maxn][maxn],dp[810][maxn],pos[810][maxn]; void read(int &x){ x=0; char c=getchar(); while(c>'9'||c<'0') c=getchar(); while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); } int main() { int N,K,x,y; scanf("%d%d",&N,&K); rep(i,0,K) rep(j,0,N) dp[i][j]=2000000000; rep(i,1,N) rep(j,1,N){ read(sum[i][j]); sum[i][j]=sum[i][j]+sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1]; } rep(i,1,N) rep(j,i+1,N) cost[i][j]=(sum[j][j]+sum[i-1][i-1]-sum[j][i-1]-sum[i-1][j])/2; rep(i,1,N) dp[1][i]=cost[1][i]; rep(i,2,K){ for(int j=N;j>=i;j--){ int L=pos[i-1][j]?pos[i-1][j]:1; int R=pos[i][j+1]?pos[i][j+1]:N; rep(k,L,R){ if(dp[i-1][k]+cost[k+1][j]<dp[i][j]){ dp[i][j]=dp[i-1][k]+cost[k+1][j]; pos[i][j]=k; } } } } printf("%d ",dp[K][N]); return 0; }
利用DP决策单调性解决:684ms。二者时间差不多。
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=4010; const int inf=0x3f3f3f3f; int sum[maxn][maxn],cost[maxn][maxn],dp[maxn],ans[maxn]; void read(int &x){ x=0; char c=getchar(); while(c>'9'||c<'0') c=getchar(); while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); } void get(int l,int r,int L,int R){ if(l>r) return ; int mid=(l+r)>>1,MID; rep(i,L+1,min(R+1,mid)){ if(ans[mid]>dp[i-1]+cost[i][mid]){ ans[mid]=dp[i-1]+cost[i][mid]; MID=i-1; } } get(l,mid-1,L,MID); get(mid+1,r,MID,R); } int main() { int N,K,x,y; scanf("%d%d",&N,&K); rep(i,1,N) rep(j,1,N){ read(sum[i][j]); sum[i][j]=sum[i][j]+sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1]; } rep(i,1,N) rep(j,i+1,N) cost[i][j]=(sum[j][j]+sum[i-1][i-1]-sum[j][i-1]-sum[i-1][j])/2; rep(i,1,N) ans[i]=cost[1][i]; rep(i,2,K){ rep(j,1,N) dp[j]=ans[j],ans[j]=inf; get(1,N,0,N-1); } printf("%d ",ans[N]); return 0; }