• 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+mle 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;
    }
    
  • 相关阅读:
    行测(爆发篇)之图形推理
    行测(爆发篇)之资料分析
    申论(准备篇)之申论思维
    行测(基础篇)之基础常识复习建议
    行测(基础篇)之汉语语法与阅读习惯梳理
    行测笔记整理
    申论之日积月累
    申论(准备篇)之大纲的五个隐藏秘密
    行测(基础篇)之中学知识回顾
    申论(准备篇)之找好方向
  • 原文地址:https://www.cnblogs.com/fusiwei/p/11736045.html
Copyright © 2020-2023  润新知