2015 CCPC 国赛K题,HDU5550(线性递推DP)

来源:互联网 发布:pycharm与python 编辑:程序博客网 时间:2024/05/20 07:34

Game Rooms

                          Time Limit: 4000/4000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)
                                                  Total Submission(s): 123    Accepted Submission(s): 34


Problem Description
Your company has just constructed a new skyscraper, but you just noticed a terrible problem: there is only space to put one game room on each floor! The game rooms have not been furnished yet, so you can still decide which ones should be for table tennis and which ones should be for pool. There must be at least one game room of each type in the building.

Luckily, you know who will work where in this building (everyone has picked out offices). You know that there will be Ti table tennis players and Pi pool players on each floor. Our goal is to minimize the sum of distances for each employee to their nearest game room. The distance is the difference in floor numbers: 0 if an employee is on the same floor as a game room of their desired type, 1 if the nearest game room of the desired type is exactly one floor above or below the employee, and so on.
 

Input
The first line of the input gives the number of test cases, T(1T100)T test cases follow. Each test case begins with one line with an integer N(2N4000), the number of floors in the building. N lines follow, each consists of 2 integers, Ti and Pi(1Ti,Pi109), the number of table tennis and pool players on the ith floor. The lines are given in increasing order of floor number, starting with floor 1 and going upward.
 

Output
For each test case, output one line containing Case #x: y, where x is the test case number (starting from 1) and y is the minimal sum of distances.
 

Sample Input
1210 54 3
 

Sample Output
Case #1: 9
Hint
In the first case, you can build a table tennis game room on the first floor and a pool game room on the second floor. In this case, the 5 pool players on the first floor will need to go one floor up, and the 4 table tennis players on the second floor will need to go one floor down. So the total distance is 9.
 

Source
The 2015 China Collegiate Programming Contest
 


国赛K题,现场赛时没有想出来,赛后看题解发现用01背包的思想就可以解决,还是见识短浅做题少。

题意:n层楼,每层楼有 t个人喜欢桌球,p个人喜欢游泳,每层楼可以修一个桌球室或者一个游泳池,

    每个人的花费为他到他喜欢的桌球室(游泳池)的最小距离,问如何规划才能使所有人的花费和最小并输出最小值。

思路:k为0或1表示桌球室或者游泳池,dp(i,j,k)表示第i层楼修k代表的房间,i的上一个与他不同的房间在第j层。

   对于 j<i-1,dp[i][j][k]=dp[i-1][j][k]+cnt[i][k^1]*(i-j)

   对于 j=i-1,dp[i][j][k]=min(mem[0][k][i-1]+sum[0][k][i-1]+cnt[i][k^1],dp[i-1][p][k^1]+add-subt+cnt[i][k^1]  | p<i-1 )

需要注意的问题:

  1.这样推的一个好处就是不需要对全体dp初始化

  2.需要用滚动数组来实现dp

  3.需要预处理,前i层喜欢桌球(游泳)的前缀和前i层所有喜欢桌球(游泳)的人到第i层花费,以实现O(1)状态转移。

  4.注意数据范围,爆int导致WA了几发。

  5.还有一种思路,留个链接:http://blog.csdn.net/playwfun/article/details/49445303

代码如下:

#include <iostream>#include <cstdio>#include <cstring>#include <string>#include <algorithm>#include <cmath>#include <vector>#include <set>#include <map>#include <stack>#include <queue>using namespace std;typedef long long LL;LL dp[2][4005][2],mem[2][2][4005],sum[2][2][4005];LL cnt[4005][2];int main (){    int t,n;    scanf("%d",&t);    for(int kk=1;kk<=t;kk++){        scanf("%d",&n);        for(int i=1;i<=n;i++)            scanf("%lld%lld",&cnt[i][0],&cnt[i][1]);        mem[0][0][0]=sum[0][0][0]=0;        mem[1][0][n+1]=sum[1][0][n+1]=0;        mem[0][1][0]=sum[0][1][0]=0;        mem[1][1][n+1]=sum[1][1][n+1]=0;        for(int i=1;i<=n;i++){            sum[0][0][i]=sum[0][0][i-1]+cnt[i][0];            sum[0][1][i]=sum[0][1][i-1]+cnt[i][1];            mem[0][0][i]=mem[0][0][i-1]+sum[0][0][i-1];            mem[0][1][i]=mem[0][1][i-1]+sum[0][1][i-1];        }        for(int i=n;i>=1;i--){            sum[1][0][i]=sum[1][0][i+1]+cnt[i][0];            sum[1][1][i]=sum[1][1][i+1]+cnt[i][1];            mem[1][0][i]=mem[1][0][i+1]+sum[1][0][i+1];            mem[1][1][i]=mem[1][1][i+1]+sum[1][1][i+1];        }        dp[0][1][0]=cnt[1][0]+cnt[2][1];        dp[0][1][1]=cnt[2][0]+cnt[1][1];        int cur=0,pre=1;        for(int i=3;i<=n;i++){            cur^=1;            pre^=1;            for(int j=1;j<i;j++){                for(int k=0;k<=1;k++){                    if(j!=i-1)                        dp[cur][j][k]=dp[pre][j][k]+cnt[i][k^1]*(i-j);                    if(j==i-1){                        dp[cur][j][k]=mem[0][k][i-1]+sum[0][k][i-1]+cnt[i][k^1];                        for(int p=1;p<j;p++){                            LL tot=sum[1][k][(j+p+1)/2+1]-sum[1][k][j+1];                            LL add=mem[0][k][j+1]-mem[0][k][(j+p+1)/2]                                    -sum[0][k][(j+p+1)/2]*abs(j+1-(j+p+1)/2);                            LL subt=mem[1][k][(j+p+1)/2+1]-mem[1][k][j+1]                                    -sum[1][k][j+1]*abs(j-(j+p+1)/2)+((j+p+1)/2+1-p)*tot;                            dp[cur][j][k]=min(dp[cur][j][k],dp[pre][p][k^1]+add-subt+cnt[i][k^1]);                        }                    }                }            }        }        LL ans=min(dp[cur][1][0],dp[cur][1][1]);        for(int i=2;i<n;i++)            ans=min(min(dp[cur][i][0],dp[cur][i][1]),ans);        printf("Case #%d: %lld\n",kk,ans);    }    return 0;}/*3410 54 32 94 6210 54 3810 54 32 94 610 54 32 94 6*/


0 0