P4401 [IOI 2007] Miners 矿工配餐 题解

题目传送门

我的博客

前言

大爬好啊!我最喜欢大爬了!

思路

这道题,当看到“以使得两个煤矿的产煤量的总和最大。”的时候就想到是DP。具体怎么实现呢?

我们令 \(dp_{i,a_1,a_2,b_1,b_2}\) 表示当前运送第 \(i\) 个食品车,煤矿 \(1\) 前两次运送的食品类型分别为 \(a_1,a_2\),煤矿 \(2\) 前两次运送的食品类型分别为 \(b_1,b_2\) 的最大值。

那么初始状态即为 \(dp_{0,0,0,0,0}=0\)

然后你兴高采烈的写完了这个代码,发现MLE了!

再一看内存限制,\(17.58MB\)

于是你发现其实每次的 \(dp_i\) 只与 \(dp_{i-1}\) 有关。所以可以采用滚动数组的形式减少空间。

时间复杂度 \(O(n \times 4^4)\)

代码(压行版)

const int N=1e5+10;
const int INF=0x3f3f3f3f;
int n,a[N],dp[5][5][5][5][5],ans;
int calc(int x,int y,int now){//根据题意进行计算本次运送食品,煤的产出单位
//这个就不压行了,要不太难看 
	if(!x&&!y) return 1;
	if(!x){
		if(y==now) return 1;
		return 2;
	}
	if(x==y&&x==now) return 1;
	if(x!=now&&y!=now&&x!=y) return 3;
	return 2;
}
signed main(){
	n=Read();
	for(int i=1;i<=n;i++){
		char c;c=getchar();
		if(c=='M') a[i]=1;
		if(c=='F') a[i]=2;
		if(c=='B') a[i]=3;
	}
	memset(dp,-INF,sizeof(dp));//赋初值! 
	dp[0][0][0][0][0]=0;//初始状态 
	for(int i=1;i<=n;i++) for(int a1=0;a1<=3;a1++) for(int a2=0;a2<=3;a2++) for(int b1=0;b1<=3;b1++) for(int b2=0;b2<=3;b2++)
		{ 
			if(dp[(i-1)%2][a1][a2][b1][b2]==-INF) continue;//如果没有上一个状态,不更新 
			int t1=0,t2=0;
			t1=dp[(i-1)%2][a1][a2][b1][b2]+calc(a1,a2,a[i]);//这个食品车给煤矿 1 
			t2=dp[(i-1)%2][a1][a2][b1][b2]+calc(b1,b2,a[i]);//这个食品车给煤矿 2 
			dp[i%2][a2][a[i]][b1][b2]=max(dp[i%2][a2][a[i]][b1][b2],t1);
			dp[i%2][a1][a2][b2][a[i]]=max(dp[i%2][a1][a2][b2][a[i]],t2);
		} 

	for(int a1=0;a1<=3;a1++) for(int a2=0;a2<=3;a2++) for(int b1=0;b1<=3;b1++) for(int b2=0;b2<=3;b2++) ans=max(ans,dp[n%2][a1][a2][b1][b2]);
	printf("%d\n",ans);
	return 0; 
}
posted on 2025-11-07 14:49  _Liuliuliuliuliu  阅读(0)  评论(0)    收藏  举报