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]\),那么转移方程为:
接下来就是如何计算 \(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\) 走,那么价值就可以这样表示:
我们肯定不能硬算求和,需要通过预处理来实现 \(O(1)\) 的计算。观察式子:
因此我们可以预处理前缀和 \(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\) 走:
当 \(l \neq 1\) 且 \(r = n\) 时,全部的人都要往 \(l-1\) 走:
当 \(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;
}

浙公网安备 33010602011771号