题目链接
题目思路
前言
看一眼确实像一个背包问题,但是会发现数据是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;
}