AOJ 2571: Floating Islands
マッチングの最小化をする。
想定解法では変なことをしてO(N^2)であるが、見るからにMonge DPのDivide and Conquerテクなので、終了。
距離の和の計算で嵌った。
(そういえばDPの式ですが、dp[i][j]: i個目の余った点(サイズ1以上)でj個覆ったときの最小コスト、なので、ちょっと想定解のと違います)
#include<stdio.h> #include<algorithm> using namespace std; pair<int,int>b[4100]; long long dp[4100][4100]; int ra[4100]; int sa[4100]; int ss[4100]; long long sum[4100]; int lb[4100]; inline long long calc(int a,int l,int r){ if(l>r)return 0; long long ret=0; if(l<lb[a]){ ret+=(long long)sa[a]*(lb[a]-l)-(sum[lb[a]]-sum[l]); } if(r+1<lb[a]){ ret-=(long long)sa[a]*(lb[a]-r-1)-(sum[lb[a]]-sum[r+1]); } if(r>=lb[a]){ ret+=(sum[r+1]-sum[lb[a]])-(long long)sa[a]*(r-lb[a]+1); } if(l>lb[a]){ ret-=(sum[l]-sum[lb[a]])-(long long)sa[a]*(l-lb[a]); } //printf("%d %d %d: %lld\n",a,l,r,ret); return ret; } void solve(int t,int a,int b,int c,int d){ if(a>=b)return; int M=(a+b)/2; long long val=999999999999999LL; int at=-1; for(int i=c;i<d&&i<=M;i++){ if(M-i>ss[t])continue; long long cost=dp[t][i]+calc(t,i,M-1); if(val>cost){ val=cost;at=i; } } dp[t+1][M]=val; solve(t,a,M,c,at+1); solve(t,M+1,b,at,d); } int main(){ int a; while(scanf("%d",&a),a){ for(int i=0;i<a;i++){ int p,q;scanf("%d%d",&p,&q); b[i]=make_pair(p,q); } std::sort(b,b+a); long long ret=b[a-1].first-b[0].first; int R=0; int S=0; int st=0; for(int i=0;i<a;i++){ int req=2; if(i==0||i==a-1)req--; if(req<b[i].second){ sa[S]=b[i].first; ss[S++]=b[i].second-req; }else if(req>b[i].second){ ra[R++]=b[i].first; } st+=b[i].second; } if(st<2*a-2){ printf("-1\n");continue; } for(int i=0;i<=R;i++)sum[i]=0; for(int i=0;i<R;i++)sum[i+1]=sum[i]+ra[i]; for(int i=0;i<S;i++){ lb[i]=lower_bound(ra,ra+R,sa[i])-ra; } for(int i=0;i<=S;i++){ for(int j=0;j<=R;j++)dp[i][j]=99999999999999LL; } dp[0][0]=0; int now=0; for(int i=0;i<S;i++){ solve(i,0,R+1,0,R+1); // now+=ss[i]; // if(now>=R)now=R; // for(int j=0;j<=now;j++)printf("%lld ",dp[i+1][j]); // printf("\n"); } ret+=dp[S][R]; printf("%lld\n",ret); } }