UVA12991 Game Rooms
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
思路
数据范围一眼dp。
这道题状态和转移也都是一眼就看得出来的。
直接设 \(f_{i,0/1}\) 表示对于前 \(i\) 层楼而言,第 \(i\) 层楼为球桌/泳池的最小代价,那么转移可以直接出来
其中 \(cost(i,j,0/1)\) 表示 \([i,j]\) 这一段全为 \(0/1\) 的代价,这一段的代价由两部分组成,第一部分是 \([l,mid]\) 全部跑到 \(l-1\) 另一部分是 \([mid+1,r]\) 全部跑到 \(r+1\) 去了,那么这里很明显需要 \(O(1)\) 求出的是两个式子的值:
那么对此要考虑二阶前缀和。
维护一个前缀和数组(以 \(1\) 为例)\(S_1\) ,再维护一个二阶前缀和数组 \(S_2\) ,使得:
因此上面的第一个式子就可以 \(O(1)\) 求得:
第二个式子也是同理,维护两个后缀和就可以了,只不过算的时候系数要注意一下
因此这道题时间复杂度 \(O(TN^2)\) 从理论上来讲过不了,但实际都是这么做的,还都过了?
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=4e3+10,inf=1e18;
int c[N][2],n;
int ps1[N][2],ps2[N][2];
int ss1[N][2],ss2[N][2];
int f[N][2];
int costp(int l,int r,int k){//[l,r]全为k ,单增
k^=1;
return ps1[r][k]-ps1[l-1][k]-(l-1)*(ps2[r][k]-ps2[l-1][k]);
}
int costs(int l,int r,int k){//[l,r]全为k ,单减
k^=1;
return ss1[l][k]-ss1[r+1][k]-(n-r)*(ss2[l][k]-ss2[r+1][k]);
}
int calc(int l,int r,int k){
if(l==1 && r==n) return inf;
if(l==1) return costs(l,r,k);
if(r==n) return costp(l,r,k);
int mid=(l+r)>>1;
return costp(l,mid,k)+costs(mid+1,r,k);
}
void init(){
ps1[0][0]=ps1[0][1]=0;
ps2[0][0]=ps2[0][1]=0;
ss1[n+1][0]=ss1[n+1][1]=0;
ss2[n+1][0]=ss2[n+1][1]=0;
memset(f,0x3f,sizeof(f));
f[0][0]=0;
f[0][1]=0;
}
int caseid;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--){
caseid++;
init();
cin>>n;
for(int i=1;i<=n;i++) cin>>c[i][0]>>c[i][1];
for(int i=1;i<=n;i++){
ps1[i][0]=ps1[i-1][0]+c[i][0]*i;
ps1[i][1]=ps1[i-1][1]+c[i][1]*i;
ps2[i][0]=ps2[i-1][0]+c[i][0];
ps2[i][1]=ps2[i-1][1]+c[i][1];
}
for(int i=n;i>=1;i--){
ss1[i][0]=ss1[i+1][0]+c[i][0]*(n-i+1);
ss1[i][1]=ss1[i+1][1]+c[i][1]*(n-i+1);
ss2[i][0]=ss2[i+1][0]+c[i][0];
ss2[i][1]=ss2[i+1][1]+c[i][1];
}
for(int i=1;i<=n;i++){
for(int j=0;j<i;j++){
f[i][0]=min(f[i][0],f[j][1]+calc(j+1,i,0));
f[i][1]=min(f[i][1],f[j][0]+calc(j+1,i,1));
}
}
cout<<"Case #"<<caseid<<": "<<min(f[n][0],f[n][1])<<endl;
}
return 0;
}

浙公网安备 33010602011771号