洛谷 P2530 [SHOI2001]化工厂装箱员 题解【DP】【枚举】
点击量:146
动态规划的多维枚举。
题目描述
118号工厂是世界唯一秘密提炼锎的化工厂,由于提炼锎的难度非常高,技术不是十分完善,所以工厂生产的锎成品可能会有3种不同的纯度,A:100%,B:1%,C:0.01%,为了出售方便,必须把不同纯度的成品分开装箱,装箱员grant第1次顺序从流水线上取10个成品(如果一共不足10个,则全部取出),以后每一次把手中某种纯度的成品放进相应的箱子,然后再从流水线上顺序取一些成品,使手中保持10个成品(如果把剩下的全部取出不足10个,则全部取出),如果所有的成品都装进了箱子,那么grant的任务就完成了。
由于装箱是件非常累的事情,grant希望他能够以最少的装箱次数来完成他的任务,现在他请你编个程序帮助他。
输入输出格式
输入格式:
第1行为n(1<=n<=100),为成品的数量
以后n行,每行为一个大写字母A,B或C,表示成品的纯度。
输出格式:
仅一行,为grant需要的最少的装箱次数。
输入输出样例
输入样例#1:11ABCABCABCAB输出样例#1:3
题解:
用数组f[m][i][j][k]来维护装到第m格,目前手上有i个A,j个B,k个C。那么每次转移可以把i/j/k中的一个变成0,把m加上这个数,并且更新i,j,k在(m,m+i/j/k]这一段新出现的次数。例如选了i,就转移为f[m+i][i’][j+j’][k+k’],i’表示在(m,m+i]这一段中A出现的次数。每次暴力枚举i,j,k有多少个,只要f数组中它不是inf就可以用刷表法转移。
遇到多重状态时一定要多撕烤思考,当范围小时想想高维DP,范围大时也可以高维DP骗分。
Code:
#include<cstdio>
#include<cstring>
int min(int x,int y)
{
return x<y?x:y;
}
int f[110][11][11][11];
int a[110];
int b[110],c[110];
int main()
{
memset(f,0x3f,sizeof(f));
int n;
scanf("%d",&n);
char s[20];
for(int i=1;i<=n;i++)
{
scanf("%s",s);
a[i]=a[i-1]+(s[0]=='A');
b[i]=b[i-1]+(s[0]=='B');
c[i]=c[i-1]+(s[0]=='C');
}
int up=min(10,n);//注意控制手上不到10个的状态
f[up][a[up]][b[up]][c[up]]=0;
for(int i=up,flag=0;i<=n&&flag<3;i++)
{
for(int A=0;A<=10;A++)
for(int B=0;B<=10;B++)
for(int C=0;C<=10;C++)
if(f[i][A][B][C]!=0x3f3f3f3f)
{
int nxt=min(i+A,n);
int aa=a[nxt]-a[i];
int bb=B+b[nxt]-b[i];
int cc=C+c[nxt]-c[i];
f[nxt][aa][bb][cc]=min(f[nxt][aa][bb][cc],f[i][A][B][C]+1);//把A装箱
nxt=min(i+B,n);
aa=A+a[nxt]-a[i];
bb=b[nxt]-b[i];
cc=C+c[nxt]-c[i];
f[nxt][aa][bb][cc]=min(f[nxt][aa][bb][cc],f[i][A][B][C]+1);
nxt=min(i+C,n);
aa=A+a[nxt]-a[i];
bb=B+b[nxt]-b[i];
cc=c[nxt]-c[i];
f[nxt][aa][bb][cc]=min(f[nxt][aa][bb][cc],f[i][A][B][C]+1);
}
if(i==n)
{
flag++;
i--;
}
}
printf("%d\n",f[n][0][0][0]);
return 0;
}
说点什么