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

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
  
  
1 2 10 5 4 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
 


国赛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;
}

/*
3
4
10 5
4 3
2 9
4 6
2
10 5
4 3


8
10 5
4 3
2 9
4 6
10 5
4 3
2 9
4 6
*/


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值