HDU3591 01 背包 + 完全背包 + 多重背包 混用


0)01 背包,完全背包, 多重背包,混用。

解题关键有两个。

一个是将小倩付钱和老板找零分别做一个背包,小倩多重背包,老板无限背包,这样做可以的原因是,两个背包面值相同时对应的状态总可以作为一个不变的桥梁。

另一个是,处理小倩的多重背包时,采用二进制优化可以显著缩短时间,不然因为数据过多容易超时(哪怕代码有错算不出正确数据,只要超时了,就会返回超时)。

二进制优化的思想,就是利用任何数都可以被2^k(1、2、4、8...组合得到)表示,所以每个面值搜索1到n遍(n是该面值拥有数),可以降低成lgn次,得到新的一组面值(注意不是正好的2^k时,比如10 ==  1 2 4 + 3,最后的3不要忘记),每个面值只有一张,再进行一次01背包即可。


啊,对了,还有题目最后,给出这样一句话:

But Xiaoqian is a low-pitched girl , she wouldn’t like giving out more than 20000 once. 

告诉我们上限是20000,然而至今没有读懂它的意思,这不是告诉我们价换次数不超过20000次吗?那和费用有什么关系...


1) 二进制优化代码(31ms AC)

#include <iostream>
#include <string.h>
#include <stdio.h>

using namespace std;
const int maxn=110;
const int maxm=20010;
const int INF=0x3f3f3f3f;
struct Stuff{
    int v;
    int c;
}stuff[maxn];
int bag_one[maxm];
int bag_two[maxm];
int main()
{
    int n,t;
    int kase=0;
    while(scanf("%d%d",&n,&t)){
        if(n==0){
            break;
        }
        kase++;
        for(int i=1;i<=n;i++){
            scanf("%d",&stuff[i].v);

        }
        for(int i=1;i<=n;i++){
            scanf("%d",&stuff[i].c);
        }
        
        for(int i=1;i<=maxm;i++){
            bag_one[i]=INF;
            bag_two[i]=INF;
        }
        
        //memset(bag_one,INF,sizeof(bag_one));//现在是31ms,如果将memset的两个初始化,替代上面的一个for,将是46ms
        //memset(bag_two,INF,sizeof(bag_two));
        bag_one[0]=0;
        bag_two[0]=0;
        //小倩付钱时,如果更改stuff,那么就把找零钱的for循环放在小倩付钱的for前面
        //找零时,每种硬币个数不限,是完全背包
         for(int i=1;i<=n;i++){
            for(int j=stuff[i].v;j<=20000;j++){
                bag_two[j]=min(bag_two[j],bag_two[j-stuff[i].v]+1);//找零j时,找零的最小个数
            }
        }
        for(int i=1;i<=n;i++){
            //如果数量足够多,就相当于完全背包
			if(stuff[i].c*stuff[i].v>=20000){
				for(int j=stuff[i].v;j<=20000;j++){
					bag_one[j]=min(bag_one[j],bag_one[j-stuff[i].v]+1);
				}
				continue;
			}
			for(int k=1;k<=stuff[i].c;k*=2){//k是可以==stuff[i].c的
				for(int j=20000;j>=stuff[i].v*k;j--){
					bag_one[j]=min(bag_one[j],bag_one[j-stuff[i].v*k]+k);
				}
				stuff[i].c-=k;//!!!!!注意位置
			}
			for(int j=20000;j>=stuff[i].v*stuff[i].c;j--){
					bag_one[j]=min(bag_one[j],bag_one[j-stuff[i].v*stuff[i].c]+stuff[i].c);
            }
        }
        int minn=INF;
        for(int j=t;j<=20000;j++){
                minn=min(minn,bag_one[j]+bag_two[j-t]);
        }
        int flag=-1;
        if(minn>=INF){
            printf("Case %d: %d\n",kase,flag);
        }
        else{
            printf("Case %d: %d\n",kase,minn);
                        //cout<<coux<<endl;
        }
    }

    return 0;
}


2)二进制优化               //与1)不同在于将所有面值处理一遍得到的所有新的面值(每个面值只有一张),存到新的数组,然后再使用,156ms,AC

#include <iostream>
#include <string.h>
#include <stdio.h>

using namespace std;
const int maxn=130;
const int maxm=23000;
const int INF=0x3f3f3f3f;
struct Stuff{
    int v;
    int c;
}stuff[maxn];
int bag_one[maxm];
int bag_two[maxm];
int main()
{
    int n,t;
    int kase=0;
    while(scanf("%d%d",&n,&t)){
        if(n==0){
            break;
        }
		
        kase++;
        
        for(int i=1;i<=n;i++){
            scanf("%d",&stuff[i].v);
     
        
        }
        for(int i=1;i<=n;i++){
            scanf("%d",&stuff[i].c);
        }
        memset(bag_one,INF,sizeof(bag_one));
        memset(bag_two,INF,sizeof(bag_two));
        bag_one[0]=0;
        bag_two[0]=0;
		Stuff stuff_new[maxm];
		int cur=1;
        for(int i=1;i<=n;i++){
			int j;
			for(j=1;j<=stuff[i].c;j*=2){
				stuff_new[cur].v=j*stuff[i].v;
				stuff_new[cur].c=j;
				cur++;
			}
			if(j>stuff[i].c){
				stuff_new[cur].v=(stuff[i].c-j/2)*stuff[cur].v;
				stuff_new[cur].c=stuff[i].c-j/2;
				cur++;
			}
        }
      
		
        for(int i=1;i<cur;i++){
            for(int j=20000;j>=stuff_new[i].v;j--){
				bag_one[j]=min(bag_one[j],bag_one[j-stuff_new[i].v]+stuff_new[i].c);//fuqian 
			}
		}

        for(int i=1;i<=n;i++){
            for(int j=stuff[i].v;j<=20000;j++){
                bag_two[j]=min(bag_two[j],bag_two[j-stuff[i].v]+1);//找零j时,找零的最小个数
            }
        }
        
        int minn=INF;
      
        for(int j=t;j<=20000;j++){
            if(minn>=bag_one[j]+bag_two[j-t]){
                minn=min(minn,bag_one[j]+bag_two[j-t]);
               
            }
        }

        int flag=-1;
        if(minn>=INF){
            printf("Case %d: %d\n",kase,flag);
        }
        else{
            printf("Case %d: %d\n",kase,minn);
                        //cout<<coux<<endl;
        }
    }

    return 0;
}

3)朴素算法,没有二进制优化,上限是蒙对了,正好在时限内AC,811ms

#include <iostream>
#include <string.h>
#include <stdio.h>

using namespace std;
const int maxn=130;
const int maxm=23000;
const int INF=0x3f3f3f3f;
struct Stuff{
    int v;
    int c;
}stuff[maxn];
int bag_one[maxm];
int bag_two[maxm];
int main()
{
    int n,t;
    int kase=0;
    while(scanf("%d%d",&n,&t)){
        if(n==0){
            break;
        }
        kase++;
        int sum=0;
        for(int i=1;i<=n;i++){
            scanf("%d",&stuff[i].v);
            sum+=stuff[i].v;//所有面值加一遍构成的上限,事后反证得,这样做并没有科学依据..蒙对数据的上限了
        }
        for(int i=1;i<=n;i++){
            scanf("%d",&stuff[i].c);
        }
        memset(bag_one,INF,sizeof(bag_one));
        memset(bag_two,INF,sizeof(bag_two));
        bag_one[0]=0;
        bag_two[0]=0;
        for(int i=1;i<=n;i++){
		if((stuff[i].c*stuff[i].v)>t+sum){
				for(int j=stuff[i].v;j<=t+sum;j++){
					bag_one[j]=min(bag_one[j],bag_one[j-stuff[i].v]+1);//付钱j时,付的最少个数
				}
			}
		else{
			for(int j=t+sum;j>=stuff[i].v;j--){//这里是倒序
				for(int k=1;k<=stuff[i].c&&k*stuff[i].v<=j;k++){
					bag_one[j]=min(bag_one[j],bag_one[j-k*stuff[i].v]+k);//付钱j时,付的最少个数
				}
			}
		}
        }
        for(int i=1;i<=n;i++){
            for(int j=stuff[i].v;j<=t+sum;j++){
                bag_two[j]=min(bag_two[j],bag_two[j-stuff[i].v]+1);//找零j时,找零的最小个数
            }
        }
        int minn=INF;
        for(int j=t;j<=t+sum;j++){
            if(minn>=bag_one[j]+bag_two[j]){
                minn=min(minn,bag_one[j]+bag_two[j-t]);
            }
        }
        int flag=-1;
        if(minn>=INF){
            printf("Case %d: %d\n",kase,flag);
        }
        else{
            printf("Case %d: %d\n",kase,minn);
        }
    }

    return 0;
}


4)

Description

In the country of ALPC , Xiaoqian is a very famous mathematician. She is immersed in calculate, and she want to use the minimum number of coins in every shopping. (The numbers of the shopping include the coins she gave the store and the store backed to her.) 
And now , Xiaoqian wants to buy T (1 ≤ T ≤ 10,000) cents of supplies. The currency system has N (1 ≤ N ≤ 100) different coins, with values V1, V2, ..., VN (1 ≤ Vi ≤ 120). Xiaoqian is carrying C1 coins of value V1, C2 coins of value V2, ...., and CN coins of value VN (0 ≤ Ci ≤ 10,000). The shopkeeper has an unlimited supply of all the coins, and always makes change in the most efficient manner .But Xiaoqian is a low-pitched girl , she wouldn’t like giving out more than 20000 once. 
 

Input

There are several test cases in the input. 
Line 1: Two space-separated integers: N and T. 
Line 2: N space-separated integers, respectively V1, V2, ..., VN coins (V1, ...VN) 
Line 3: N space-separated integers, respectively C1, C2, ..., CN 
The end of the input is a double 0. 
 

Output

Output one line for each test case like this ”Case X: Y” : X presents the Xth test case and Y presents the minimum number of coins . If it is impossible to pay and receive exact change, output -1.
 

Sample Input

    
    
3 70 5 25 50 5 2 1 0 0
 

Sample Output

    
    
Case 1: 3


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值