zzuli 1432 背包again(求最小不能被得到的价值

1432: 背包again

时间限制: 1 Sec   内存限制: 128 MB
提交: 147   解决: 36
[ 提交][ 状态][ 讨论版]

题目描述

Gy最近学习了01背包问题,无聊的他又想到了一个新的问题,给定n个物品的价值,和01背包一样,每个物品只能选1次或0次,求最小不能被得到的价值。

输入

第一行一个正整数T(T <= 100),表示有T组数据。

每组数据输入格式如下:

第一行为一个正整数N(N<=100),表示物品个数。

第二行N个正整数,表示每个物品的价值vi(1<=vi<=1000000)

输出

共输出T行,即每组数据相应答案。

样例输入

2
3
2 4 8
4
1 2 4 8

样例输出

1
16


本题看似dp问题,但观察物品价值的范围即可发现普通的背包解法是无法在规定时间内解决的,所以需要换一种思路来考虑


看第二组样例数据,明显是在提示我们去想1 2 4 8 16 32。。。这个数列a[1]=1, a[i]=a[i-1]*2,这个数列的特点是其前i项,可以通过加法组合得到所有小于第i+1项的数。

例如对于前3项1 2 4这三个数,

1=1

2=2

3=1+2

4=4

5=1+4

6=2+4

7=1+2+4

而a[4]=8,1~7都可得到。

方便记忆,将1 2 4 8 16。。。这个数列记为数列cp,先通过以下程序打出cp数列,因为已经给定物品价值范围不会大于1000000,所以只需算到刚大于1000000那一项即可

#include<iostream>
using namespace std;
int main()
{
	int i=1;
	freopen("out.txt","w",stdout);
	cout<<i<<',';
	while(true)
	{
		i=(i<<1);
		cout<<i<<',';
		if(i>1000000)	break;
	}
	return 0;
}

从输出的文本中得到以下序列,范围内的有20个

1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,

接下来,对于本题,将输入序列记为ain,先将其排序后记为a

求解步骤如下:

以a为1 2 3 4为例

1、尽量从a序列中依序取出cp数列中的元素;

(即取出了1 2 4,1 2 4可表示出1~7,记ans=8

2、从a剩下的数中取最小值m

1)若m>ans,最终结果即为ans,完成!

2)若m<=ans,ans+=m,从a中移除m,goto 2

(即对于a中经过第1步,剩下的数字为3,3<8,那么:8=3+5=3+(1+4), 9=3+6=3+(2+4), 10=3+7=3+(1+2+4),即ans=3+8=11

若对于a为1 2 4 9,因为9>8,8就无法取到了,所以得到最终答案为8.


代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<list>
using namespace std;

int main()
{
	vector<int> ain;
	list<int> a;
	int num;
	int cp[50]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576};
	while(cin>>num)
	{
		while(num--!=0)
		{
			int n,pb;
			cin>>n;
			ain.clear();
			a.clear();
			for(int i=1;i<=n;++i)
			{
				scanf("%d",&pb);
				ain.push_back(pb);
			}
			sort(ain.begin(),ain.end());
			for(int i=0;i!=ain.size();++i)
			{
				a.push_back(ain[i]);
			}
			int ans=1,j=0;
			for(list<int>::iterator i=a.begin();i!=a.end()&&j<20; )
			{
				if(*i==cp[j])
				{
					ans=cp[j+1];
					i=a.erase(i);
					++j;
					continue;
				}
				++i;
			}
			for(list<int>::iterator i=a.begin();i!=a.end();++i)
			{
				if(*i>ans)
				{
					break;
				}
				else
				{
					ans+=(*i);
				}
			}
			cout<<ans<<endl;
		}
	}
	return 0;
}

Ps:代码中用到了vector,后来又换成list,因为vector的表操作时间消耗是O(n),而list仅花费常数时间


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值