SHOI 2001 化工厂装箱员

洛谷 P2530 [SHOI2001]化工厂装箱员

洛谷传送门

题目描述

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复制

输出 #1复制

题解:

一道最优化题,而且有状态有转移,我们考虑DP。

特意请教了大佬@JZYShuraK,什么时候我们考虑DP呢?首先是DP计数(数数题恶心得要命)然后是最优化题,尤其是,那种不需要了解过程,只需要输出结果的(比如本题,我们压根不需要知道到底什么时候拿走了什么、多少物品,只需要知道最少拿多少次(即我们要统计的答案))。这个时候可以果断选择DP。

注意到这道题的数据范围:N是1-100的。这个数据范围甚至可以支持我们跑\(O(n^4)\)的算法,无论是时间复杂度还是空间复杂度都是如此。这道题一开始把我难住的一个点是状态的设置。总觉得怎么设置都很令人不满意。后来终于恍然:一维两维搞不清楚状态,多开几维不就成了?反正空间够用。

那么,我们的状态便设置成:

\(dp[i][j][k][m]\)表示一共\(i\)个物品,手中还剩\(j\)\(A\)\(k\)\(B\)\(m\)\(C\)时的最小装箱次数。

然后我们考虑初值和答案:因为要求最小值,所以答案显然设置成最大值。特殊地,不难发现,\(dp[0][0][0][0]=0\)。而根据状态,我们的最终答案就是:\(dp[n][0][0][0]\)

接下来就是状态转移了:

在我们设计一个\(DP\)算法的时候,我们需要牢记:\(DP\)算法的实质其实是一个决策的过程,也就是说,对于每一个状态和阶段,我们如何去根据具体的情况而选择一个当前情况下的最优决策。而由于\(DP\)的无后效性的影响,这个决策是不会影响到下一个决策的,也就是说,我们每一次进行决策的时候都选择了最优的一种情况,那么最后的答案显然就是最优的。

那么针对于本题,我们的决策应该是什么呢?

就是:放还是不放。即取出来先在手中存着,还是取出来直接放进去。

于是我们就发现,对于第\(i\)件物品,如果我们存着的话,那么有:
\[ dp[i][j][k][m]=dp[i-1][j-1][k][m] \]
(此处演示的是第\(i\)件物品为\(A\)的情况,其他情况同理)

如果装箱的话:
\[ dp[i][0][k][m]=min(dp[i][0][k][m],dp[i][j][k][m]+1); \]
(注意,因为我们每次装箱要装进去所有的同种物品,所以第二维是0)

在我们枚举的时候,还要考虑\(j+k+m\le 10\)

代码如下:

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n;
int dp[110][11][11][11];
char opt[110];
int main()
{
    memset(dp,0x3f,sizeof(dp));
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        cin>>opt[i];
    dp[0][0][0][0]=0;
    for(int i=1;i<=n;i++)
        for(int j=0;j<=10;j++)
            for(int k=0;k<=10;k++)
                for(int m=0;m<=10;m++)
                {
                    if(j+k+m>10)
                        continue;
                    if(opt[i]=='A' && j)
                        dp[i][j][k][m]=dp[i-1][j-1][k][m];
                    if(opt[i]=='B' && k)
                        dp[i][j][k][m]=dp[i-1][j][k-1][m];
                    if(opt[i]=='C' && m)
                        dp[i][j][k][m]=dp[i-1][j][k][m-1];
                    dp[i][0][k][m]=min(dp[i][0][k][m],dp[i][j][k][m]+1);
                    dp[i][j][0][m]=min(dp[i][j][0][m],dp[i][j][k][m]+1);
                    dp[i][j][k][0]=min(dp[i][j][k][0],dp[i][j][k][m]+1);
                }
    printf("%d",dp[n][0][0][0]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/fusiwei/p/11736045.html