• 【t077】宝物筛选


    Time Limit: 1 second
    Memory Limit: 128 MB

    【问题描述】

    小FF找到了王室的宝物室,里面堆满了无数价值连城的宝物……这下小FF可发财了。但是这里的宝物实在是太多了,小FF的采集车
    似乎装不下那么多宝物。看来小FF只能含泪舍弃其中的一部分宝物了……小FF对洞穴里的宝物进行了整理,他发现每样宝物都
    有一件或者多件,他粗略的估算了下每样宝物的价值,之后开始了宝物筛选工作:小FF有一个最大载重为W的采集车,洞穴里总
    共有n种宝物,每种宝物的价值为v[i],重量为w[i],每种宝物有m[i]件。小FF希望在采集车不超载的前提下,选择一些宝物装
    进采集车,使得它们的价值和最大。 【数据范围】
    对于30%的数据:n≤∑m[i]≤10^4; 0≤W≤10^3
    对于100%的数据:n≤∑m[i]≤10^5;0≤W≤4*10^4; 1≤n≤100. 

    【输入格式】

    第一行为一个整数N和W,分别表示宝物种数和采集车的最大载重。
    接下来n行每行三个整数,其中第i行第一个数表示第i类品价值,第二个整数表示一件该类物品的重量,第三个整数为该类物品数量。 

    【输出格式】

    输出仅一个整数ans,表示在采集车不超载的情况下收集的宝物的最大价值。

    Sample Input

     4  20
        3  9  3
        5  9  1
        9  4  2
        8  1  3
    
    
    
    
    

    Sample Output

     47

    【题解】

    这题的同种物品数量很多。很多嘛。肯定就要用特殊的办法。那就是,那就是,那就是二进制优化。

    比如有一个类型的物品,有50个

    31 的二进制是11111

    而111111 == 63 > 50

    所以我们先获得2^0 2^1 2^2 2^3 2^4.

    然后用50-31;

    得19;

    然后我们再获得19;

    总共我们获得了 1 2 4 8 16 19 这几个数字;

    然后 我们就可以用这几个数字组合成1-50中任意的数字了。

    于是我们生成若干个物品,他们的价值分别是1*c[i],2*c[i],4*c[i].....19*c[i],重量也同样扩大,即1*w[i],2*w[i],4*w[i]...19*w[i];

    接下来解释重点。

    为什么用这几个数字就能组合成1-50中的任意数字?

    首先 1-31这些数字是肯定可以的了。

    因为1-31就是xxxxx(5个数字)这样的情况。x可能等于1或0.这是其二进制形式。如果某个x等于1就相应的加上2^d就可以啦。

    然后就是32-50

    比如40

    这些数字全部加起来等于50;

    那么也就是说我们要除去一系列的数,且这些数的和等于10;

    而1-31这些数字都能由1 2 4 8 16得来。所以 10也就没问题了。用这些数字中的一些数字组合成10,然后去掉就可以了。

    综上 1 2 4 8 16 19这些数字可以组合成1-50中的任意一个数字。

    生成那些物品后,就可以当做0/1背包问题来解决了

    【代码】

    #include <cstdio>
    #include <cstring>
    
    int n,m,temp[30],w[2000],c[2000],nn = 0,f[50000];
    
    void init()
    {
    	temp[0] = 1;//2^0 == 1 
    	for (int i = 1;i <= 20;i++) //获取2^i 存入temp[i]中 
    		temp[i] = temp[i-1] * 2;
    }
    
    void input_data()
    {
    	scanf("%d%d",&n,&m);
    	for (int i = 1;i <= n;i++) //输入n个物品的信息 
    		{
    			int cc,ww,num;
    			scanf("%d%d%d",&cc,&ww,&num);	
    			int now =0;
    			while (num >= temp[now]) //temp[i]表示2^i now一开始等于0 
    				{
    					nn++;
    					w[nn] = temp[now]*ww; //扩大成一个新的物品。 
    					c[nn] = temp[now]*cc;
    					num-=temp[now];
    					now++;	
    				}
    			if (num > 0) //还可能剩下一些物品 聚成一个就好 
    				{
    					nn++;
    					w[nn] = num*ww;
    					c[nn] = num*cc;	
    				}
    		}
    }
    
    void get_ans()
    {
    	memset(f,0,sizeof(f));
    	for (int i = 1;i <= nn;i++)
    		for (int j = m;j >= w[i];j--) //用0/1背包的更新方式更新解就可以啦 
    			if (f[j] < f[j-w[i]] + c[i])
    				f[j] = f[j-w[i]] + c[i];
    }
    
    void output_ans()
    {
    	printf("%d
    ",f[m]);	
    }
    
    int main()
    {
    	//freopen("F:\rush.txt","r",stdin);
    	//freopen("F:\rush_out.txt","w",stdout);
    	init();
    	input_data();
    	get_ans();
    	output_ans();
    	return 0;
    }



  • 相关阅读:
    20165308实验三 敏捷开发与XP实践实验报告
    20165308 《Java程序设计》第9周学习总结
    20165308 四则运算2
    20165308 实验二 Java面向对象程序设计
    20165308第八周学习总结
    20165308课上补做
    20165308 结队训练
    20165308《Java程序设计》第7周学习总结
    2018-2019-1 20165320 《信息安全系统设计基础》第七周学习总结
    2018-2019-1 20165320 《信息安全系统设计基础》第六周学习总结
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7632393.html
Copyright © 2020-2023  润新知