竞赛题目讲解-【Central Europe 1996】装箱问题

【Central Europe 1996】装箱问题


Description
一个工厂制造的产品形状都是长方体,它们的高度都是h,长和宽都相等,一共有六个型号,他们的长宽分别为1*1, 2*2, 3*3, 4*4, 5*5, 6*6。这些产品通常使用一个 6*6*h 的长方体包裹包装然后邮寄给客户。因为邮费很贵,所以工厂要想方设法的减小每个订单运送时的包裹数量。他们很需要有一个好的程序帮他们解决这个问题从而节省费用。现在这个程序由你来设计。

Input
输入文件包括几行,每一行代表一个订单。每个订单里的一行包括六个整数,中间用空格隔开,分别为1*1至6*6这六种产品的数量。输入文件将以6个0组成的一行结尾。
Output
除了输入的最后一行6个0以外,输入文件里每一行对应着输出文件的一行,每一行输出一个整数代表对应的订单所需的最小包裹数。

Sample Input

0 0 4 0 0 1 
7 5 1 0 0 0 
0 0 0 0 0 0 

Sample Output

2 
1 

题目解析
这道题是贪心的经典题,可以作为初学的练习题目。
首先是处理多组数据——用无限循环(while(true) 或 for(;;)),在内部定义一个bool变量Break 初值为true,输入的同时,判断当前输入数据是否有非零数,若有,把Break改为false。最后,如果Break保持true不变,则break退出无限循环。

然后在内部编写程序。这里有两种思想,作者的较为易懂,先讲解这一种:
由于放置各种不同型号的盒子时区别太大,我把每一个型号分开放置。但是我们需要决定一个放置顺序——我们知道越小的盒子是越容易放置的,而若小盒子占用的位置太多,大盒子则无法放置,因此大盒子要优先放置
首先放置6 * 6的盒子,很明显,一个盒子必须占用一个箱子,并且不能放置其他的盒子!所以6 * 6占用的箱子的数目就是它本身的数目
再放置5 * 5的盒子。同样,一个盒子就需要一个箱子,但是会留下一些空隙,这些位置还能放置11个1 * 1的盒子,为了保证不浪费,我们要尽量把箱子塞满。那么放置n个5 * 5盒子所需要 11n 个1 * 1盒子。于是我们在1 * 1 盒子的原本数目中减去这些,不用担心负数
4 * 4 依然每个盒子要占一个箱子,会有20格的空隙,能放置5个 2 * 2 箱子,也能放置20个 1 * 1 箱子。这时我们根据优先放置大盒子的规则,要先用 2 * 2 的盒子填好,若没有 2 * 2 箱子,但还有空位,就用 1 * 1 的盒子来填。
3 * 3 的盒子最复杂。首先4个 3 * 3 盒子能把一个箱子填满,而余下部分同样需要一个箱子,那么它需要的箱子数量为它的数量除以4过后向上取整的结果。由于箱子中放置的 3 * 3 盒子的数量不同,2 * 2盒子能够放置的个数是难以确定的,因此我们可以用一个数组 three_two,three_two[i]表示有i个 3 * 3 盒子时,可以放置的 2 * 2 盒子的个数。而 1 * 1 的盒子的个数则是36(箱子大小)减9(3 * 3 盒子大小)乘 3 * 3 盒子个数减4(2 * 2盒子大小)乘 2 * 2 盒子个数
1 * 1 盒子与 2 * 2 盒子可以一起计算,可以把4个 1 * 1 盒子算作一个 2 * 2 盒子,剩余的 1 * 1 盒子也算作一个 2 * 2 盒子。所需箱子为 2 * 2 盒子的个数除以9所得的结果向上取整

另外一种更加简洁,但较为复杂:
从上面的分析,我们知道—— 6 * 6 、 5 * 5 、 4 * 4 以及 3 * 3 的数量除以4向上取整的数量之和是它们必须占用的箱子数量,于是我们可以预先算出这一结果。我们把 1 * 1 和 2 * 2 的盒子用于“塞缝”。对于 2 * 2 的盒子,可以“给 4 * 4 盒子塞缝”,一个4 * 4 盒子的空隙能用5个 2 * 2 盒子填满;3 * 3 盒子空隙所需要的 2 * 2 盒子个数可以用一个数组存。1 * 1 的盒子用来最后填缝。
若空隙已经填满了,还剩余了 1 * 1 和 2 * 2 的盒子,同样按照作者的做法,将 1 * 1 处理成 2 * 2 盒子,按照9个 2 * 2 盒子装满1个箱子的规则,计算出答案。


样例程序
Accepted 作者的做法:

/*Lucky_Glass*/
#include
int main()
{
    while(true)
    {
        bool Break=true;
        int box[7]={},ans=0;
        for(int i=1;i<=6;i++)
        {
            scanf("%d",&box[i]);
            if(box[i]) Break=false;
        }
        if(Break) break;
        /*six*/
        ans+=box[6];
        box[6]=0;
        /*five*/
        ans+=box[5];
        box[1]-=box[5]*11;
        box[5]=0;
        /*four*/
        ans+=box[4];
        if(box[2]>=5*box[4])
            box[2]-=5*box[4];
        else
        {
            box[1]-=(box[4]*5-box[2])*4;
            box[2]=0;
        }
        /*three*/
        ans+=(box[3]+3)/4;
        int three_two[4]={0,5,3,1},box3=box[3]%4;
        box[3]=0;
        if(box[2]>=three_two[box3])
        {
            box[2]-=three_two[box3];
            box[1]-=9*(4-box3)-three_two[box3]*4;
        }
        else
        {
            box[1]-=9*(4-box3)-box[2]*4;
            box[2]=0;
        }
        /*two*/
        if(box[1]>0) box[2]+=(box[1]+3)/4;
        ans+=(box[2]+8)/9;
        printf("%d\n",ans);
    }
    return 0;
}

Accepted 别人的做法(一开始看不懂):

#include 
int main() 
{ 
    int N,a,b,c,d,e,f,y,x;
    int u[4]={0,5,3,1}; 
    while(1)
    { 
        scanf("%d%d%d%d%d%d",&a,&b,&c,&d,&e,&f); 
        if (a==0 && b==0 && c==0 && d==0 && e==0 && f==0) 
            break; 
        N=f+e+d+(c+3)/4; 
        y=5*d+u[c%4];
        if(b>y) N+=(b-y+8)/9;
        x=36*N-36*f-25*e-16*d-9*c-4*b; 
        if(a>x) N+=(a-x+35)/36; 
        printf("%d\n",N); 
    } 
    return 0;
}

The End

Thanks for reading


你可能感兴趣的:(#贪心算法,-,贪心也是需要决策的#)