奇怪的背包问题增加了
https://ac.nowcoder.com/acm/problem/204441
分析:这一题咋一看像dp,但仔细想想又不能像传统的dp做,因为背包大小是2的30次方,肯定不能开个这个大的数组,然后就开始像是不是什么奇怪的位操作dp。转念一想要不先看看状态转移。对于任何一个物体我要不要选它,想了一会发现根本没办法从哪一个状态转移到另一个状态。这样一看是不是不能从0往背包大小dp,而是从背包大小往0开始dp,依旧无从下手,但是联想到贪心的做法。。
我先维护一个数组a记录指数出现的次数,数组vis记录指数出现的次数,数组b记录每一个指数。
然后我从0到29遍历a数组,指数可以合并向上升的就向上升,一直到29,然后判断a[30]的值,大于0说明存在解,等于0说明无解。
有解的情况下我让vis[30] = 1, 然后关键的来了
vis[30] = 1;
for (int i = 29; i >= 0;i--){
// if(a[i + 1]-vis[i+1]<=0) break;
a[i] += (max(0,a[i + 1]-vis[i+1])) * 2;
}
我将多余的数子不断往下还原,也就是a[i+1] - vis[i+1] 就是第i+1位指数多余的数,乘2就可以还原到第i位。
此时a数组中的值则代表不需要这个指数的数量,因为需要的已经转换成a[30]固定了。
for (int i = 1; i <= n;i++){
// debug(a[b[i]]);
if(a[b[i]]>0){
ans += '0';
a[b[i]]--;
}
else{
ans += '1';
}
}
最后当a[i]>0时说明这个b[i]可以不需要 , a[i]<=0说明这个b[i]被选中。