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

本文介绍了一个楼层游戏室布局优化问题,目标是最小化员工到达喜爱的游戏室的距离总和。通过01背包思想解决该问题,并提供了详细的解题思路及代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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.youkuaiyun.com/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
*/


### 关于2023年江苏CCPC的相关信息 根据已知的引用资料,虽然未直接提及2023年江苏CCPC的具体目,但可以结合其他事的经验和模式推测可能的内容结构以及需要注意的关键点。 #### 事概述 2023年的ICPC际大学生程序设计竞南京站的比报告显示,正式日期为 **2023年11月5日周日**,参队伍总数达到331支[^1]。尽管该报告主要关注的是ICPC而非CCPC,但从两者的相似性和时间线来看,CCPC也可能在同一时间段内举行类似的活动。 #### 参考经验教训 从2021年的CCPC总结可以看出,团队在比中遇到的主要挑战包括心态调整、细节把握以及队内沟通不足等问[^2]。这些方面同样适用于任何编程竞场景下,因此无论具体目如何变化,都需要重视以下几个环节: - **签到处理效率**:快速解决简单问以建立信心并节省后续复杂任务的时间。 - **思路验证充分性**:确保初步想法经过深思熟虑后再实施编码阶段。 - **成员协作流畅度**:鼓励每位队员分享个人见解以便集体达成最优解决方案路径。 #### 示例代码风格指导 基于过往比中的实际案例分析,下面展示了一段典型的C++实现片段用于计算累加折扣后的总价逻辑[^3]: ```cpp #include <iostream> #include <algorithm> using namespace std; double calculateTotal(int n, double prices[]) { double total = 0; sort(prices, prices + n); for (int i = 0; i < n; ++i){ if(total >=400 ){ total +=prices[i]; } else if( total>=150 && total<400 ){ total +=prices[i]*0.5 ; } else if( total>=100 &&total<150 ){ total +=prices[i]*0.8 ; } else{ total +=prices[i]; } } return round(total * 100)/100 ;//保留两位小数 } int main(){ int t; cin>>t; while(t--){ int numItems; cin >>numItems; double priceList[numItems]; for(auto &price : priceList )cin>>price ; cout<<fixed <<setprecision(2)<<calculateTotal(numItems ,priceList)<<endl; } } ``` 此代码展示了如何依据不同的条件应用相应的折扣率来更新总计金额,并最终输出精确至两位小数的结果。 #### 解决方案框架建议 针对类似于“给定一系列带颜色标记数值的操作序列”,可采用堆栈数据结构辅助完成目标——即寻找满足特定约束条件下最大化的最小值问[^4]。以下是简化版伪算法描述: 1. 初始化两个空列表分别代表`S1` 和 `S2`. 2. 遍历输入数组中的每一个元素: - 如果当前存在符合条件的不同颜色配对,则执行相应操作并将结果存入`S2`; - 否则仅将其加入`S1`. 3. 完成全部迭代后返回`S2`集合里的最小项作为答案. --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值