HDU 5550 Game Rooms (dp)

来源:互联网 发布:阿里云cdn怎么收费 编辑:程序博客网 时间:2024/06/05 08:10

题目链接: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;}


          

0 0
原创粉丝点击