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

题意

有两个煤矿和三种食品车,每种食品车装的食物不同且只装一种食物,食品车每天会向两个煤矿中的一个运送食物,每天矿工都会拿当天的食物与此前两次食品车来时拿到的食物对比,如果这三次有三种食物则产出 \(3\) 单位煤,有两种食物产出 \(2\) 单位,一种产出 \(1\) 单位。预先已知食品车的类型和配送顺序,求最大总产煤量。

思路

因为每次的产煤量只与这三次的食物有关,而食物一共三种,数据很小,我们不妨直接把食物当做状态,用 \(f_{i,j,k,w,z}\) 表示当一号煤矿此前两次的食物为 \(j,k\),二号煤矿此前两次食物为 \(w,z\),当前为第 \(i\) 个食品车的总产煤量。这样可以直接转移,我们设 \(i\) 车的食物为 \(a_i\),则有 \(f_{i,k,a_i,w,z}=\max(f_{i-1,j,k,w,z}+val)\)\(f_{i,j,k,z,a_i}=\max(f_{i-1,j,k,w,z}+val)\),其中 \(val\) 表示当前情况三辆车所对应的产煤量。

这样提交上去一定全挂。我们注意到惊人的空间范围,而 \(N=10^5\),所以使用滚动数组解决。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int f[2][4][4][4][4],n;
int a[100010],ans;
int val(int x,int y,int z){
	int t=0;
	if(x&&x!=y&&x!=z)t++;
	if(y&&y!=z)t++;
	return t+1;
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		char x;
		cin>>x;
		if(x=='M')a[i]=1;
		else if(x=='F')a[i]=2;
		else a[i]=3;
	}
	memset(f,-1,sizeof(f));
	f[0][0][0][0][0]=0;
	for(int i=1;i<=n;i++){
		for(int j=0;j<4;j++){
			for(int k=0;k<4;k++){
				for(int w=0;w<4;w++){
					for(int z=0;z<4;z++){
						if(f[(i+1)%2][j][k][w][z]==-1)continue;
						f[i%2][k][a[i]][w][z]=max(f[i%2][k][a[i]][w][z],f[(i+1)%2][j][k][w][z]+val(j,k,a[i]));
						f[i%2][j][k][z][a[i]]=max(f[i%2][j][k][z][a[i]],f[(i+1)%2][j][k][w][z]+val(w,z,a[i])); 
					}
				}
			}
		}
	}
	for(int i=0;i<4;i++){
		for(int j=0;j<4;j++){
			for(int k=0;k<4;k++){
				for(int w=0;w<4;w++){
					ans=max(ans,f[n%2][i][j][k][w]);
				}
			}
		}
	}
	cout<<ans;
	return 0;
}
posted @ 2025-08-14 17:01  ImNot6Dora  阅读(10)  评论(0)    收藏  举报