HDU5550 Game Rooms(dp)
题意:
有n(4000)层楼,每层楼有1e9个人,每个人都有自己喜欢的一种运动(一共有两种),每层楼都可以开两种运动馆其中之一
如果当前开了a馆,则这一层喜欢b运动的人都要移动到最近的开b运动的楼层,代价是楼层差,让你合理安排,问你最小的代价
思路:
看http://blog.csdn.net/kirito_acmer/article/details/49700065这个代码注释吧,感觉自己肯定是想不出来的
#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; typedef long long ll; #define inf 0x7fffffff #define maxn 4050 ll a[maxn],b[maxn]; ll sum[maxn][2];//sum[i][u]表示前i层楼,性别为u的总人数 ll dsum[maxn][2];//dsum[i][u]表示前i层楼,性别为u者距离0层的距离之和 ll dp[maxn][2];//dp[i][u]表示第i层为u属性,第i+1层为另一属性,前i层不同性别到达自己的最近属性的寝室的最近距离和 ll goup(int l,int r,int sex){ //表示[l+1,r]区间sex性别要去r+1的总距离 return (sum[r][sex]-sum[l][sex])*(r+1)-(dsum[r][sex]-dsum[l][sex]); } ll godown(int l,int r,int sex){ //表示[l+1,r]区间sex性别要去l的总距离 return dsum[r][sex]-dsum[l][sex]-(sum[r][sex]-sum[l][sex])*l; } ll cnt(int l,int r,int sex){ //在[l,r]都是sex属性,且l-1与r+1都为非sex属性的条件下。 [l,r]这些楼层非sex属性的人,去自己属性寝室的最小距离。 int mid=(l+r)>>1; return godown(l-1,mid,sex)+goup(mid,r,sex); } int main() { int n,m,i,j,T,cas=0; scanf("%d",&T); while(T--) { scanf("%d",&n); sum[0][0]=sum[0][1]=0; dsum[0][0]=dsum[0][1]=0; for(i=1;i<=n;i++){ scanf("%lld%lld",&a[i],&b[i]); sum[i][0]=sum[i-1][0]+a[i]; sum[i][1]=sum[i-1][1]+b[i]; dsum[i][0]=dsum[i-1][0]+a[i]*i; dsum[i][1]=dsum[i-1][1]+b[i]*i; } memset(dp,0,sizeof(dp)); ll ans=1e18; for(i=1;i<n;i++){ dp[i][0]=goup(0,i,1); //这里先假设前i层都是性别0,i+1层是性别1所要的总距离 dp[i][1]=goup(0,i,0); for(j=1;j<i;j++){ dp[i][0]=min(dp[i][0],dp[j][1]+cnt(j+1,i,1) ); //依次使得前j层是1,使得第j,i+1都是1,这样就好状态转移了 dp[i][1]=min(dp[i][1],dp[j][0]+cnt(j+1,i,0) ); } ans=min(ans,dp[i][0]+godown(i,n,0) ); //这里每一层都要更新一下ans,而不能最后才更新,因为最后才更新的话就不能使得后面几层都相同了 ans=min(ans,dp[i][1]+godown(i,n,1) ); } cas++; printf("Case #%d: %lld\n",cas,ans); } return 0; }