装箱问题

装箱问题

Description

有一个箱子容量为v(正整数,0≤v≤20000),同时有n个物品(0<n≤30),每个物品有一个体积(正整数)。

要求从m个物品中,任取若千个装入箱内,使箱子的剩余空间为最小。

Input

第一行,一个整数v,表示箱子容量;

第二行,一个整数,表示有n个物品;

接下来n行,每行一个数,分别表示这n个物品的各自体积。

Output

输出一行,一个整数,表示箱子剩余空间。

Sample Input

24

6

8

3

12

7

9

7

Sample Output

0

方法1(动态规划)

思路:

我们把第n个数字当作阶段,把某一个数能否被已有的数字组成当作状态。

 

我们设:新组成的数字=a

    原有的数字=b

    读入的数字=c

那么有:a=b+c

 

我们可以用一个bool数组储存我们构成的数字,每读入一个新数字就循环把新数字加上原有的数字。

 

注意循环枚举原有数字时要用逆循环。

原因:这道题中每个数字只能使用一次。

若使用正循环,不可避免的在这次循环中刚加入数组的数字也会被当成原有的数字,那么同一个数字就会被加上多次,不符合题意。

因为c>0,所以a>b,所以使用逆循环就是从大往小找,就可以保证不会把刚加入数组的数字当成原有的数字,每个数也就只会被使用一次。(若题目中每个数都能使用多次,那么只需要把逆循环改为正循环)

代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n,m,sum[31];
    bool puts[20001];
    memset(puts,false,sizeof(puts));
    puts[0]=true;//边界值
    scanf("%d %d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&sum[i]);
        for(int j=n;j>=0;j--)
            if(puts[j]==true)//若j出现过
                if(j+sum[i]<=n)
                    puts[j+sum[i]]=true;
    }
    for(int i=n;i>=0;i--)//找最小值
        if(puts[i]==true)
        {
            printf("%d",n-i);
            return 0;
        }
}

方法2(队列)

把已经找到的数放入队列,每次只要访问这个队列,并把队列里每个数都加上刚读入的数就可得到新数字。

方法1中的数字是否使用一次的问题:每次确定把tail的值赋给tail2,保证从head访问到tail2,而每次加数字时都把tail+1,这样就可保证每个数字只使用一次。(若每个数字可使用多次,就从head访问到tail)

代码

#include<bits/stdc++.h>
using namespace std;
int n,v,k,tail=1,tail2,head=1,ans,num[200010];
bool f[20010];
int main()
{
    memset(f,false,sizeof(f));
    f[0]=true;//边界值 
    num[tail]=0;//边界值 
    ans=2147483647;//无穷大 
    scanf("%d %d",&v,&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&k);
        tail2=tail;
        for(int j=head;j<=tail2;j++)
            if(f[num[j]+k]==false&&(num[j]+k)<=v)//(num[j]+k)未出现过且在范围内 
            {
                tail++;//尾节点++ 
                num[tail]=num[j]+k;
                f[num[j]+k]=true; 
                ans=min(ans,(v-num[j]-k));//取最小值 
            }
    }
    printf("%d",ans);
    return 0;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值