题意:某公寓有N层楼,每层楼有Pi个游泳爱好者和Ti个乒乓球爱好者,每层楼只能建一个游泳池或者一个乒乓球场,求所有人到其运动场所的最短距离之和(两层楼之间距离为1)
2<=N<=4000,1<=Pi,Ti<=109.1~100组数据,4000MS
题解:DP[i][j][0~1]分别表示第i层建乒乓球场,游泳场,且前一个和该处运动场所不同的位置为j,通过预处理完成O(1)转移,总复杂度为O(N2)
使用滚动数组,单独考虑来自DP[i-1][0][0~1]的转移
预处理内容:前缀和S,S2[i]=∑S[j]*j*2(1<=j<=i),前缀和的前缀和(不必要)
#include<cstdio> #include<cstring> #include<iostream> using namespace std; const long long inf=100000000000000000; const int maxn=4200; int tt,n,now; long long t[maxn],p[maxn],sx[maxn],sy[maxn],s2x[maxn],s2y[maxn],s0x[maxn],s0y[maxn]; long long dp[2][maxn][2]; long long ans; int main(){ //freopen("K.in","r",stdin); //freopen("K.out","w",stdout); scanf("%d",&tt); for (int q=1;q<=tt;++q){ scanf("%d",&n); sx[0]=0;sy[0]=0;s2x[0]=0;s2y[0]=0;s0x[0]=0;s0y[0]=0; for (int i=1;i<=n;++i) {scanf("%d%d",&t[i],&p[i]);sx[i]=sx[i-1]+t[i];sy[i]=sy[i-1]+p[i];s0x[i]=s0x[i-1]+sx[i];s0y[i]=s0y[i-1]+sy[i];} for (int i=1;i<=n;++i) {s2x[i]=s2x[i-1]+t[i]*2*i;s2y[i]=s2y[i-1]+p[i]*2*i;} memset(dp,0,sizeof(dp)); now=1; for (int i=2;i<=n;++i){ for (int j=1;j<=i-2;++j) dp[now][j][0]=dp[now^1][j][0]+p[i]*(i-j); dp[now][i-1][0]=s0x[i-1]+p[i]; for (int j=1;j<=i-2;++j) dp[now][i-1][0]=min(dp[now][i-1][0],dp[now^1][j][1]+p[i]-(s2x[i-1]-s2x[(i-1+j)/2]-(sx[i-1]-sx[(i-1+j)/2])*(i+j))); for (int j=0;j<=i-2;++j) dp[now][j][1]=dp[now^1][j][1]+t[i]*(i-j); dp[now][i-1][1]=s0y[i-1]+t[i]; for (int j=1;j<=i-2;++j) dp[now][i-1][1]=min(dp[now][i-1][1],dp[now^1][j][0]+t[i]-(s2y[i-1]-s2y[(i-1+j)/2]-(sy[i-1]-sy[(i-1+j)/2])*(i+j))); now=now^1; } now=now^1; ans=inf; for (int i=1;i<=n-1;++i) {ans=min(ans,dp[now][i][1]),ans=min(ans,dp[now][i][0]);} cout<<"Case #"<<q<<": "<<ans<<endl; } return 0; }
时间: 2024-10-10 17:48:27