(\)
(Description)
传送带上按顺序传过来(N)个物品,一个有(A,B,C)三类。
每次装箱员手里只能至多拿十个,然后将手中三类物品中的一类装箱,才能接着拿或接着装箱,求完成整个序列的最少装箱次数。
- (Nin [1,100])
(\)
(Solution)
这数据范围不是搜索乱搞
-
(DP)。设(f[s][i][j][k])表示,当前已经取走了前(s)个,(A)类手里有(i)个,(B)类手里有(j)个,(C)类手里有(k)个,其余取出的全部已经装箱时,最少总装箱次数。有显然边界(f[0][0][0][0]=1)。
-
于是暴力枚举状态,复杂度是( ext O(100 imes 10^3)= ext O(10^5))的。转移考虑先将手里的东西拿满,再放下其中一类物品。即计算将要达到的位置(s'),到(s')时手里三类物品的个数(x,y,z),然后就只需要考虑放下哪一类的问题了。
f[sum][0][y][z]=min(f[sum][0][y][z],f[s][i][j][k]+1); f[sum][x][0][z]=min(f[sum][x][0][z],f[s][i][j][k]+1); f[sum][x][y][0]=min(f[sum][x][y][0],f[s][i][j][k]+1);
-
注意到达位置需要考虑序列的边界,答案即为(f[n][0][0][0])。
(\)
(Code)
#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 110
#define R register
#define gc getchar
#define inf 2000000000
using namespace std;
char c;
int n,ans=inf,a[N],f[N][11][11][11];
int main(){
scanf("%d",&n);
for(R int i=1;i<=n;++i){
c=gc();
while(!isalpha(c)) c=gc();
a[i]=(c=='A'?0:(c=='B'?1:2));
}
for(R int s=0;s<=n;++s)
for(R int i=0;i<=10;++i)
for(R int j=0;j<=10;++j)
for(R int k=0;k<=10;++k) f[s][i][j][k]=inf;
f[0][0][0][0]=0;
for(R int s=0;s<=n;++s)
for(R int i=0;i<=10;++i)
for(R int j=0;j<=10;++j)
for(R int k=0,sum,x,y,z;k<=10;++k)
if(f[s][i][j][k]<inf){
sum=min(n,s+(10-i-j-k)); x=i; y=j; z=k;
for(R int p=s+1;p<=sum;++p) a[p]==0?++x:(a[p]==1?++y:++z);
f[sum][0][y][z]=min(f[sum][0][y][z],f[s][i][j][k]+1);
f[sum][x][0][z]=min(f[sum][x][0][z],f[s][i][j][k]+1);
f[sum][x][y][0]=min(f[sum][x][y][0],f[s][i][j][k]+1);
}
printf("%d
",f[n][0][0][0]);
return 0;
}