UVA 12991

UVA12991 Game Rooms

题目描述

题意

一个 \(N\) 层的大楼,每层只有一个游戏室,可以设置一个乒乓球桌或游泳池。第 \(i\) 层有 \(T_i\) 个人喜欢乒乓球和 \(P_i\) 个人喜欢游泳。

现在要求使每个人到最近的喜欢的类型的活动室的距离的最小,且这栋大楼要有至少一个乒乓球桌和至少一个游泳池。

这里的距离指楼层差的绝对值。例如,如果一个人到他喜欢的类型的游戏室位于同一楼层,则为 \(0\);如果所需类型的最接近的游戏室正好在该员工的上方或下方一层,则为 \(1\),以此类推。

输入格式

第一行给出数据组数 \(T\)\(1\leq T\leq 100\))。接下来对于每组数据,首先一行给出 \(N\)\(2\leq N\leq 4000\)),代表该大楼一共有多少层;接下来 \(N\) 行第 \(i\) 行给出两个整数 \(T_i, P_i\)\(1\leq T_i, P_i\leq 10^9\)),代表第 \(i\) 层的人数,意义同题意

输出格式

对于第 \(x\) 组( \(x\)\(1\) 开始标号)数据的答案 \(y\),在第 \(x\) 行输出 Case #x: y

样例解释

在第一层设置乒乓球桌,在第二层设置游泳池。这样 \(5\) 个人要从第一层走到第二层, \(4\) 个人要从第二层走到第一层,距离和为 \(9\)

Translated by @Piwry

输入输出样例 #1

输入 #1

1
2
10 5
4 3

输出 #1

Case #1: 9

解题报告

首先,我们把每层的安排球桌和游泳池看成放置 \(0\)\(1\),那么最终我们会得到一个长为 \(n\)\(01\) 序列。

还是从最暴力的想法开始,我们对每一层都枚举是放 \(0\) 还是 \(1\),最后统计费用。

需要想办法去优化,我们从发掘题目的特点入手,从确定的情况开始,假设我们已有一个已确定的 \(01\) 序列,我们怎么计算总费用?

观察原思路的处理过程,它是在枚举每个点,然后对于每个点去找最近的 \(0\)\(1\)。现在以找到最近的 \(1\) 为例,我们发现原思路统计费用的过程中跑满复杂度上线的时候,出现了一段长度接近 \(n\)极大连续 \(0\),对于找最近的 \(0\) 也是同理。因此我们把思路转到如何处理这些极大连续段和。

实际上,极大连续段造成了一个方便的地方:只需考虑其中一种去到球桌和游泳池中的一种所造成的费用,因为另一种各楼层都有,总费用都是 \(0\)。这样处理整个区间就有了可能。

可以考虑 DP。我们设 \(dp_{i,0/1}\) 表示在以 \(i\) 为结尾的极长 \(0/1\) 段时,前 \(i\) 个楼层中的最小费用。

枚举极长段的开头的前一位 \(j \in [0,i-1]\),那么转移方程为:

\[dp_{i,0}=min(dp_{i,0},dp_{j,1}+cost(j+1,i,1)) \\ dp_{i,1}=min(dp_{i,1},dp_{j,0}+cost(j+1,i,0)) \]

接下来就是如何计算 \(cost(j+1,i,0/1)\)

以处理极长的连续 \(0\) 为例,设 \(01\) 序列 \(s\),存在一段极长连续 \(0\) 为区间 \([l,r]\),对于 每个 \(i \in [l,r]\),其人数为 \(T_i\),设 $mid= \dfrac {l+r}{2} $。

当$l \neq 1 $ 且 \(r \ne n\) 时,根据极长段的定义,有 \(s[l-1]=s[r+1]=1\)

楼层大于 \(mid\) 的往 \(r+1\) 走,小于等于 \(mid\) 的往 \(l-1\) 走,那么价值就可以这样表示:

\[W_{[l,r]}=\sum_{i=l}^{mid} T_i \times (i-(l-1)) + \sum_{i=mid+1}^{r} T_i \times ((r+1)-i) \]

我们肯定不能硬算求和,需要通过预处理来实现 \(O(1)\) 的计算。观察式子:

\[\sum_{i=l}^{mid} T_i \times (i-(l-1))=\sum_{i=l}^{mid} T_i \times i- (l-1) \times \sum_{i=l}^{mid} T_i \\ \sum_{i=mid+1}^{r} T_i \times ((r+1)-i)=(r+1) \times \sum_{i=mid+1}^{r} T_i - \sum_{i=mid+1}^{r} T_i \times i \]

因此我们可以预处理前缀和 \(S_k=\sum\limits_{i=1}^{k} T_i\)、前缀\(pre_k=\sum\limits_{i=1}^{k} T_i \times i\)

还有特殊情况:

\(l=1\)\(r \neq n\) 时,全部的人都要往 \(r+1\) 走:

\[W_{[1,r]}=(r+1) \times \sum_{i=1}^{r} T_i-\sum_{i=1}^{r} T_i \times i \]

\(l \neq 1\)\(r = n\) 时,全部的人都要往 \(l-1\) 走:

\[W_{[l,r]}=\sum_{i=l}^{n} T_i \times i- (l-1) \times \sum_{i=l}^{n} T_i \]

\(l=1\)\(r=n\) 时,没有解,费用设为 \(+\infin\)

处理极长 \(1\) 段同理。

这样就以 \(O(Tn^2)\) 的复杂度解决了,代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int INF=0x3f3f3f3f3f3f3f3f;
const int N=1011;

inline int read()
{
	int f=1,x=0; char ch=getchar();
	while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); }
	while(isdigit(ch))  { x=x*10+ch-'0';    ch=getchar(); }
	return f*x;
}

int n;
int w[N][2];
int dp[N][2];

int sum[N][2];
int pre[N][2];

inline void Prepare()
{
    memset(sum,0,sizeof(sum));
    memset(pre,0,sizeof(pre));

    for(int i=1;i<=n;i++)
    {
        sum[i][0]=sum[i-1][0]+w[i][0];
        sum[i][1]=sum[i-1][1]+w[i][1];
    }
    for(int i=1;i<=n;i++)
    {
        pre[i][0]=pre[i-1][0]+w[i][0]*i;
        pre[i][1]=pre[i-1][1]+w[i][1]*i;
    }
}

inline int cost(int l,int r,bool opt)
{
    if(l==1 && r==n) return INF;
    int mid=l+r>>1,ans=0;
    if(l!=1 && r!=n)
    {
        ans+=( pre[mid][opt]-pre[l-1][opt] )-( sum[mid][opt]-sum[l-1][opt] )*(l-1);
        ans+=(r+1)*( sum[r][opt]-sum[mid][opt] )-( pre[r][opt]-pre[mid][opt] );
        return ans;
    }
    if(l==1)
    {
        ans+=(r+1)*sum[r][opt]-pre[r][opt];
        return ans;
    }
    if(r==n)
    {
        ans+=( pre[n][opt]-pre[l-1][opt] )-( sum[n][opt]-sum[l-1][opt] )*(l-1);
        return ans;
    }
}

signed main()
{
	// freopen("UVA12991.in","r",stdin);
	// freopen("UVA12991.out","w",stdout);
    int Q=read();
    for(int iQ=1;iQ<=Q;iQ++)
    {
        n=read();
        for(int i=1;i<=n;i++)
          w[i][0]=read(),w[i][1]=read();
        Prepare();

        memset(dp,0x3f,sizeof(dp));
        dp[0][0]=dp[0][1]=0;
        for(int i=1;i<=n;i++)
         for(int j=0;j<i;j++)
         {
             dp[i][0]=min(dp[i][0],dp[j][1]+cost(j+1,i,1));
             dp[i][1]=min(dp[i][1],dp[j][0]+cost(j+1,i,0));
         }

        printf("Case #%lld: %lld\n",iQ,min(dp[n][0],dp[n][1]));
    }
	return 0;
}

posted @ 2025-08-30 09:29  南北天球  阅读(11)  评论(0)    收藏  举报