牛客小白月赛23 奇怪的背包问题增加了 题解(思维)

题目链接

题目思路

前言
看一眼确实像一个背包问题,但是会发现数据是1e5,那么显示最多的复杂就是o(n*log(n)),显然和普通背包没什么关联

那么就要往其他方面去思考

正题

直接说结论:如果所有元素总和sum>=(1<<30),那肯定可以凑出1<<30

我只需要把所有的数字从大到小排序,依次加入数字,肯定在某个时刻和就等于1<<30

正确性:

如果我有一个2^30那肯定在第一步就找到方案了

如果没有1<<30 ,那肯定最大的数字不大于1<<29

假设我一个一个加入,当前已经加入的数字的和是S,再加入一个数字x的时候,考虑S的二进制表示加上一个2的幂,

可能会产生进位,这个进位有可能最终使S的位数增加1,或者也可能保持不变。如果这次进位使得最终S的位数增加1,

那么这次进位肯定是类似这种情形111000+1000,也就是从x的1那一位往上的位,在S中是连续一段1,

这样的加法肯定执行完之后产生的结果肯定是2的幂次,既然最终的和不小于1<<30,那么在某一次进位的时候肯定产生了1<<30

这个题目还是比较考虑思维的,要多总结

代码

#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std;
const int maxn=1e5+5;
int t,m,x,res,ans[maxn];
pair<int,int> a[maxn];
void init(){
	res=1<<30;
	memset(ans,0,sizeof(ans));
}
int main(){
	scanf("%d",&t);
	while(t--){
		init();
		scanf("%d",&m);
		for(int i=1;i<=m;i++){
			scanf("%d",&x);
			a[i].fi=1<<x;
			a[i].se=i;
		}
		sort(a+1,a+1+m,greater<pair<int,int> >());
		for(int i=1;i<=m;i++){
			if(res>=a[i].fi){
				res=res-a[i].fi;
				ans[a[i].se]=1;
			}
		}
		if(res==0){
			for(int i=1;i<=m;i++){//注意没有空格 
				printf("%d",ans[i]);
			}
			printf("\n"); 
		}else{
			printf("impossible\n");
		}
	}
	return 0;
} 
发布了68 篇原创文章 · 获赞 2 · 访问量 2245

猜你喜欢

转载自blog.csdn.net/m0_46209312/article/details/105239028