信息学奥赛一本通 1226:装箱问题 | OpenJudge NOI 4.6 19:装箱问题

【题目链接】

ybt 1226:装箱问题
OpenJudge NOI 4.6 19:装箱问题

【题目考点】

1. 贪心

【解题思路】

该题说是三维立方体,实际上无论是包裹还是产品,高度都是h,因而不用考虑高度,这实际上是二维平面上的问题。

1. 贪心选择性质的证明

贪心选择:选择最大的可以装入该包裹的产品装入该包裹

  1. 证明:存在最优解包含第一次的贪心选择。即存在最优解,第一个包裹中包含最大产品。

假设所有最优解都不包含第一次的贪心选择,即第一个包裹C中不包含最大的产品x。
最大的产品x一定存在于某个包裹内,记那个包裹为N。我们可以将N中的所有产品和C中的所有产品完全交换。总包裹数量不变,第一个包裹C中存在产品x,这与假设相悖,假设不成立,原命题得证。

  1. 证明:前k步都做贪心选择,存在最优解包含第k+1步的贪心选择。

是否做贪心选择区别在于:当前拿到一个最大的可以装入当前包裹C的产品x,是将这个产品放在当前包裹C中,还是将其放在一个新包裹N中。贪心选择是将其放在当前包裹C中。
使用反证法:假设对所有最优解,在装第k+1个产品时,当前关注的包裹C可以装入的最大产品为x,此时不进行贪心选择,不选择x,接下来只会装入大小小于x的产品。也可以说,当前包裹C中,产品x的数量不会更多。
下面考虑在N中包括x的部分产品能否和C中的一些产品或空位交换,且两包裹仍能装得下。如果可以,那么说明第k+1步进行了贪心选择,选择了产品x,总包裹数量不变,也是最优解。假设不成立,原命题得证。

  • 如果N中x的数量大于C中x的数量,且x在C中与N中都是最大的产品,那么可以将N与C中的所有产品整体交换。
    x为4*4、5*5、6*6都满足这一情况。或C中3*3的个数比N中3*3的个数更少。
    例:x为5*5,在第k+1步往C中装产品时,没有按照贪心选择将5*5的产品装入C。后来5*5的产品装入了N,C中又装了一些产品。将C与N的所有产品交换后,C中存在贪心选择,总包裹数仍然最少,说明存在最优解在第k+1步进行了贪心选择,假设不成立,原命题得证。
    在这里插入图片描述
    以下为N中x的数量小于等于C中x的数量的情况:
  • 如果x是1*1的产品,那么C当时可以放1*1的产品但不放,存在一些位置空着,C中一定有可以放下1*1的空位。可以完成交换。
    在这里插入图片描述
  • 如果x是2*2的产品,那么C当时可以放2*2的产品但不放,去放1*1的产品或空着。那么在C中一定可以选择出2*2的区域,其中可以是1*1的产品也可以是空位,与x进行交换。
    在这里插入图片描述
  • 如果x是3*3的产品,说明先前加入C的是若干个3*3的产品。
  1. 如果C中已经有3个3*3的产品,剩下3*3的位置,这部分位置的产品或空位都可以与x进行交换。
    在这里插入图片描述
  2. 如果C中已经有2个3*3的产品,且剩余的空间中摆了至多3个2*2的产品。
    N中有2个3*3产品与至多3个2*2产品,此时,把N中的1个3*3与C中的2个2*2与1个1*1交换,结果为C中有3个3*3与1个2*2,N中有1个3*3与5个2*2,这种交换是可行的。
    同理,如果N中有1个3*3与至多5个2*2,用N中的1个3*3交换C中的2个2*2与1个1*1是可行的。
    例:将N中的红色3*3产品x与C中的蓝色两个2*2产品与1个1*1产品进行交换。交换 后一些1*1产品位置发生变化。
    在这里插入图片描述
  3. 如果C中已经有1个3*3产品,剩余空间摆了至多5个2*2的产品。N中有1个3*3产品与至多5个2*2的产品,用N中的1个3*3交换C中的2个2*2与1个1*1是可行的。
    如果待交换的产品没有那么多,可以用空位代替。
    例:将N中的红色3*3产品x与C中的蓝色两个2*2产品与1个1*1产品进行交换。交换 后一些1*1产品位置发生变化。
    在这里插入图片描述
  • x不可能是4*4或更大的产品,如果是,C中可以放x但不放,C中的x只能是0个,而N中的x有1个,这是前面已经讨论过的“如果N中x的数量大于C中x的数量”的情况。
    因此N中包括x的部分产品总能和C中的一些产品或空位交换位置。说明第k+1步进行了贪心选择,选择了产品x,总包裹数量不变,也是最优解。因而假设不成立,原命题得证。
2. 具体做法

考虑各种情况下,剩下的空间最多可以放下几个各种产品,总结成表格

已有最多放几个3*3最多放几个2*2最多放几个1*1
1个6*6000
1个5*50011
1个4*40520
1个3*33527
2个3*32318
3个3*3119
4936

此这基础上,每多放一个2*2,1*1可放个数减4。
观察规律,1*1可以放的个数就是总面积6*6减去所有放入其中的产品的总面积。
在已有3*3的情况下,每多放入一个3*3,2*2可以放入的个数减2。
这里只需要设数组记录放入1个某大小产品后,其他大小产品可以放入的数量。

从大到小遍历各个产品,找到一个产品,开一个新包裹将其放入。如果剩余位置还可以放产品,那么放入对应产品,改变剩余位置可放产品的数量。直到没有位置可放或没有足够的产品可放为止。再从大到小找下一个产品,开一个新包裹将其放入。重复上述过程,直到看完所有产品为止。

【题解代码】

解法1:贪心(本人原创)
#include <bits/stdc++.h>
using namespace std;
int status[8][4];//status[i][j]:包裹中已有1个大小为i*i的产品,此时可以最多放入多少个j*j的产品 
void initStatus()
{//status[0]:包裹中没有产品时,各种产品最多可以放多少个 
    status[2][2] = 8;
    status[3][2] = 5, status[3][3] = 3;
    status[4][2] = 5;
    for(int i = 1; i <= 6; ++i)
        status[i][1] = 36 - i*i;
}
int pro[8];//pro[i]:i*i产品的数量
int main() 
{
    initStatus(); 
    while(true)
    {
        int sum = 0, bag = 0, rem[8];//sum:总产品数量  bag:包裹数 rem[i]:当前剩余空间能放几个i*i 
        for(int i = 1; i <= 6; ++i)
        {
            cin >> pro[i];
            sum += pro[i];
        }
        if(sum == 0)//如果6个数都是0,那么加和为0,要跳出循环 
            break;
        for(int i = 6; i >= 1; --i)
        {
            while(pro[i] > 0)
            {
                bag++;//用一个新包裹放i*i
                pro[i]--;
                for(int j = 1; j <= 3; ++j)//获取当前剩余空间情况 
                    rem[j] = status[i][j];
                int j = 3;
                while(j >= 1)
                {
                    if(rem[j] > 0 && pro[j] > 0)//如果可以放j*j的产品 
                    {
                        pro[j]--;//放一个产品 
                        if(j == 3)
                        {
                            rem[3]--;
                            rem[2] -= 2;
                            rem[1] -= 9;
                        }
                        else if(j == 2)
                        {
                            rem[2]--;
                            rem[1] -= 4;
                        }
                        else//j == 1
                            rem[1]--;
                    }
                    else
                        --j;
                }
            }   
        }
        cout << bag << endl;
    }  
    return 0;
}
解法2:手动完成贪心过程

思路来自zqhf123博客:zqhf123 1226:装箱问题
由于箱子种类不多,我们可以手动完成每一步的贪心行为。先装大产品,看占了多少空间,剩下多少空间。再看小产品。逐次算出当前可以给下一个产品留出的空位。

#include<bits/stdc++.h>
using namespace std;
int bag;//总包裹数 
int k[4] = {0, 5, 3, 1};//k[i]表示3*3的产品有i个时(k[0]表示有4个时)剩余空间可以放2*2产品的个数   
int pro[8], p2, p1;//p2:2*2空位数 p1:1*1空位数 
int main()
{ 
    while(true)
    {
        for(int i = 1; i <= 6; ++i)
            cin >> pro[i];
 	    if(pro[1] == 0 && pro[2] == 0 && pro[3] == 0 && pro[4] == 0 && pro[5] == 0 && pro[6] == 0) 
            break;
 	    bag = pro[6] + pro[5] + pro[4] + ceil(pro[3]/4.0);//6*6和5*5和4*4一定是一个占一个箱子,而3*3 4个占一个箱子
        p2 = 5*pro[4]+k[pro[3]%4];//k[pro[3]%4]:有pro[3]%4个3*3在一个包裹中时,剩下的位置可以放2*2的数目  
 	    if(pro[2] > p2)//2*2的个数比我们留出来为2*2的空间个数多,就需要为2*2另开箱子
            bag += ceil((pro[2]-p2)/9.0);//多出来的2*2箱子应该占用的新箱子数 
 	    p1 = 36*bag-36*pro[6]-25*pro[5]-16*pro[4]-9*pro[3]-4*pro[2];//求出当前包裹中所有剩下的可以放1*1的位置数 
        if(pro[1] > p1)//如果1*1的个数,比我们留出的空间多就需要另开空间 
	       bag += ceil((pro[1]-p1)/36.0);//将多出来的另开箱子 
	    cout << bag << endl;
    }
    return 0;
}
  • 8
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
信息学奥赛一本通1255:迷宫问题是一个关于迷宫的问题。这个问题要求通过广搜算法来解决迷宫问题,找到走出迷宫的路径。具体来说,迷宫可以看成是由n×n的格点组成,每个格点只有两种状态, "." 和 "#" 。其中 "." 代表可通行的路径,"#" 代表不可通行的墙壁。通过广搜算法,我们可以搜索从起点到终点的路径,找到一条合法的路径即可。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [信息学奥赛一本通 1255:迷宫问题 | OpenJudge NOI 2.5 7084:迷宫问题](https://blog.csdn.net/lq1990717/article/details/124721407)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [c++信息学奥赛一本通1215题解](https://download.csdn.net/download/Asad_Yuen/87357807)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [信息学奥赛一本通(1255:迷宫问题)](https://blog.csdn.net/lvcheng0309/article/details/118879231)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值