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;
}