HDU 5550 Game Rooms (dp)

本文介绍了一种解决HDU 5550问题的方法,通过动态规划来确定每层楼最优的娱乐室配置,以使所有爱好者的总路程最短。该算法使用了预处理技巧,将复杂度降低到了O(n²)。

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

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5550

题目:一共n层楼,每层楼都要建一个娱乐室,共有两种(球馆、游泳馆)可以选择,且n层楼中至少有1个球馆和游泳馆。现每层楼有Ti个球类爱好者,Pi个游泳爱好者,他们都要去各自的娱乐室娱乐,求一个建造方案,使得他们走的总路程的最小。(假设A在3楼,要去游泳。若游泳馆在3楼,则A走的路程为0;若游泳馆在2楼或4楼,则路程为1,;若在1楼或5楼,则路程为2.以此类推)。

思路:令dp(i, j)表示已经安排了前i层的人的最小花费,j为0或1,表示娱乐室的类别。我们让某一段连续楼层(从k开始)一直到i全为娱乐室j。易知,第k-1层和第i+1层均为j^1。

           那么dp(i, j) = min{ dp(k, j^1) + dist(k+1, i) }, 其中dist(k+1, i)表示安排第k+1层到第i层人的最小花费,因为k+1到i层的娱乐室都一样,所以可以在常数时间内算出。总复杂度为O(n²)。

          dist(k+1, j)的算法如下


#include<cstdio>
#include<cstring>
#include<vector>
#include<iostream>
#include<set>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 4000 + 5;
const ll INF = 1e15 + 10;
int T[maxn], P[maxn], n;
ll sumT[maxn], sumP[maxn], calT[maxn], calP[maxn];
ll dp[maxn][2];

ll toLeft(int L, int R, ll *sum, ll *cal) {
  return (cal[R] - cal[L-1]) - (L-1) * (sum[R] - sum[L-1]);
}

ll toRight(int L, int R, ll *sum, ll *cal) {
  return (R+1)*(sum[R]-sum[L-1]) - (cal[R] - cal[L-1]);
}

ll dist(int L, int R, int p) { // p为0代表L——R区间中全是球馆,p为1代表L——R区间中全是游泳馆
  
  if(L == 1 && R == n) return INF; //n层楼全是一种是不合法的
  
  else if(L == 1) { //只能去右边(即楼上)
    if(p == 1)
      return toRight(L, R, sumT, calT);
    return toRight(L, R, sumP, calP);
  }
  
  else if(R == n) { //只能去左边(即楼下)
    if(p == 1)
      return toLeft(L, R, sumT, calT);
    return toLeft(L, R, sumP, calP);
  }
  
  else { //去楼上楼下皆可,因此区间左半部分去楼下,右半部分去楼上
    int x = (L + R) >> 1;
    if(p == 1)
      return toLeft(L, x, sumT, calT) + toRight(x+1, R, sumT, calT);
    return toLeft(L, x, sumP, calP) + toRight(x+1, R, sumP, calP);
  }
  
}

int main() {
  int test, kase = 0;
  scanf("%d", &test);
  while(test--) {
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) {
      scanf("%d%d", &T[i], &P[i]);
      sumT[i] = sumT[i-1] + T[i];
      sumP[i] = sumP[i-1] + P[i];
      calT[i] = calT[i-1] + (ll)i*T[i];
      calP[i] = calP[i-1] + (ll)i*P[i];
    }
    dp[0][0] = dp[0][1] = 0;
    for(int i = 1; i <= n; i++)
      for(int j = 0; j <= 1; j++) {
        dp[i][j] = INF;
        for(int k = 0; k < i; k++)
          dp[i][j] = min(dp[i][j], dp[k][j^1] + dist(k+1, i, j));
      }
    printf("Case #%d: %lld\n", ++kase, min(dp[n][0], dp[n][1]));
  }
  return 0;
}


          

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值