$[\ SHOI\ 2001\ ]\ $化工厂装箱员


\(\\\)

\(Description\)


传送带上按顺序传过来\(N\)个物品,一个有\(A,B,C\)三类。

每次装箱员手里只能至多拿十个,然后将手中三类物品中的一类装箱,才能接着拿或接着装箱,求完成整个序列的最少装箱次数。

  • \(N\in [1,100]\)

\(\\\)

\(Solution\)


这数据范围不是搜索乱搞

  • \(DP\)。设\(f[s][i][j][k]\)表示,当前已经取走了前\(s\)个,\(A\)类手里有\(i\)个,\(B\)类手里有\(j\)个,\(C\)类手里有\(k\)个,其余取出的全部已经装箱时,最少总装箱次数。有显然边界\(f[0][0][0][0]=1\)

  • 于是暴力枚举状态,复杂度是\(\text O(100\times 10^3)=\text 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\n",f[n][0][0][0]);
  return 0;
}

猜你喜欢

转载自www.cnblogs.com/SGCollin/p/9671600.html