UVA12991 Game Rooms - 动态规划 - 二阶前缀和 -

题目链接

题解:
其实本题和区间 dp 关系不大,内核就是一个普通dp
考虑泳池和乒乓球,每种设施在楼中一定是连续的一段(长度可以为1),可能有多段
对于每个极大的这样的段(即两种设施的分界点)进行dp
dp[i][0/1]表示考虑到第 i 层楼,最后一段是水池/球,对应的最优解
dp[i][0] = dp[j][1] + cost(j+1,i,1)(另一种转移同理)
下面我们的问题就是计算cost(l,r,0/1)即如果[l,r]都用水池/球的话,这段对答案产生的贡献。
显然只需要算另一种没有在这段楼里面添加的器材(比如如果全为水池,我只需要算球的贡献)
又显然可知,令mid=(l+r)/2,则[l..mid]肯定是去l-1[mid+1..r]肯定去r+1,当然要先把l=1r=nl=1且r=n判掉(注意l=1且r=n无解,取inf)
手算一下发现我们实际要求的是一个“阶梯和”,即①[l..mid]:(mid-l+1)*p[l]+(mid-l)*p[l+1]+..+p[mid] 对于②[mid+1..r]:p[mid+1]+...+(r-mid)*p[r]
发现这两种其实是不同的,这里我们采用二阶前缀和
sum[i] = sum[i-1]+t[i],如果summ[i] = summ[i-1] + sum[i],我们得到的是summ[n] = n*t[n] + ... + 1*t[1]的形式,我们用这个来计算②,方法就是summ[r] - summ[mid] - (r-(mid+1)+1) * sum[mid]
同理我们维护一个后缀和,和后缀和的后缀和,就可以计算①了

一个很坑的点是多测,对前缀和不需要初始化,但对后缀和需要,因为每次n可能不同,suf[n+1]可能不为0

代码:

// by Balloons
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Madoka"<<endl
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)

using namespace std;

typedef long long LL;
#define int LL

const int inf = 1e18,maxn=4005;

int n,t[maxn],p[maxn];	// t 球 p 水 
int preft[maxn][2],prefp[maxn][2];	// t[i]的前缀;前缀的前缀(1*a[1]+2*a[2]+..) p[i]的前缀 前缀的前缀 
int suft[maxn][2],sufp[maxn][2];	// 后缀 后缀的后缀 
int dp[maxn][maxn];	// dp[i][0/1] 到第 i 楼,最后一段是 水/球 

int cst(int l,int r,int type){	// type==0 -> p[i] type==1 -> t[i]
	if(l==r)return type==0?p[l]:t[l];
	int mid = l+r>>1;	
	if(l == 1 && r == n)return 1e18;
	if(r == n){
		if(type == 0)return sufp[l][1];
		else return suft[l][1];
	}
	if(l == 1){
		if(type == 0)return prefp[r][1];
		else return preft[r][1];
	}
	// [l..mid] (mid-l+1)*p[l]+(mid-l)*p[l+1]+..+p[mid]  [mid+1..r] (r-mid)*p[r]+...+p[mid+1]
	// 			sufp[l][1] - sufp[mid+1][1] - (mid-l+1) * sufp[mid+1][0];
	//			prefp[r][1] - prefp[mid][1] - (r-mid) * prefp[mid][0];
	if(type == 0){
		int t1 = sufp[l][1] - sufp[mid+1][1] - (mid-l+1) * sufp[mid+1][0];
		int t2 = prefp[r][1] - prefp[mid][1] - (r-mid) * prefp[mid][0];
		return t1 +t2;
	}
	if(type == 1){
		int t1 = suft[l][1] - suft[mid+1][1] - (mid-l+1) * suft[mid+1][0];
		int t2 = preft[r][1] - preft[mid][1] - (r-mid) * preft[mid][0];
		return t1 +t2;
	}
}

int tot=0;

void solve(){
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)scanf("%lld%lld",&t[i],&p[i]);
	for(int i=1;i<=n;i++)preft[i][0] = preft[i-1][0] + t[i],prefp[i][0] = prefp[i-1][0] + p[i];
	for(int i=1;i<=n;i++)preft[i][1] = preft[i-1][1] + preft[i][0], prefp[i][1] = prefp[i-1][1] + prefp[i][0]; 

	suft[n+1][0] = suft[n+1][1] = sufp[n+1][0] = sufp[n+1][1] = 0;	//!!!!!
	for(int i=n;i>=1;i--)suft[i][0] = suft[i+1][0] + t[i],sufp[i][0] = sufp[i+1][0] + p[i];
	for(int i=n;i>=1;i--)suft[i][1] = suft[i+1][1] + suft[i][0],sufp[i][1] = sufp[i+1][1] + sufp[i][0];
	
	for(int i=1;i<=n;i++)dp[i][0] = dp[i][1] = inf;
	
	dp[0][0] = dp[0][1] = 0;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=i-1;j++){
			dp[i][0] = min(dp[i][0],dp[j][1]+cst(j+1,i,1));
			dp[i][1] = min(dp[i][1],dp[j][0]+cst(j+1,i,0));
		}
	}
	int ans = min(dp[n][1], dp[n][0]);
	printf("Case #%lld: %lld\n",++tot,ans);
}

signed main(){
//	freopen("UVA12991.in","r",stdin);
//	freopen("UVA12991.out","w",stdout);
	int te;scanf("%lld",&te);
	while(te--)solve();

	return 0;
}


posted @ 2022-08-10 11:08  SkyRainWind  阅读(77)  评论(0编辑  收藏  举报