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\) 层楼为球桌/泳池的最小代价,那么转移可以直接出来

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

其中 \(cost(i,j,0/1)\) 表示 \([i,j]\) 这一段全为 \(0/1\) 的代价,这一段的代价由两部分组成,第一部分是 \([l,mid]\) 全部跑到 \(l-1\) 另一部分是 \([mid+1,r]\) 全部跑到 \(r+1\) 去了,那么这里很明显需要 \(O(1)\) 求出的是两个式子的值:

\[\sum \limits_{i=l}^r (i-l+1)\cdot a_{0/1,i}\\ \sum \limits_{i=r}^l (r+1-i)\cdot a_{0/1,i} \]

那么对此要考虑二阶前缀和。

维护一个前缀和数组(以 \(1\) 为例)\(S_1\) ,再维护一个二阶前缀和数组 \(S_2\) ,使得:

\[S_{1,i}=\sum \limits_{j=1}^i a_{1,j}\\ S_{2,i}=\sum \limits_{j=1}^i j\cdot a_{1,j} \]

因此上面的第一个式子就可以 \(O(1)\) 求得:

\[\sum \limits_{i=l}^r (i-l+1)\cdot a_{0/1,i}=S_{2,r}-S_{2,l-1}-(l-1)\cdot(S_{1,r}-S_{1,l-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;
}
posted @ 2025-07-29 16:26  shencheng4014  阅读(8)  评论(0)    收藏  举报