题意:给定n,n是由一系列数的和组成,这些数是2^a,2^b,2^c。。。。最多有k个,你的目的是让a,b,c….中最大的数最小,如果这样的数列有多个,输出字典序最大的那个。
思路:二进制之所以唯一是因为它的每一个二进制位都只能使用一次,比如23=2^4+2^2+2^1+2^0,二进制在每位都使用一次且保证最大的数最小的情况下,去唯一的表示了一个数,那么我们假设每个二进制位能用无数多次,那么每个数可以表示成无数多种,比如23=2^3+2^3+2^2+2^1+2^0还有23=2^2*5+2^1+2^0,这样最高位就越靠左,好了,不扯了。
现在看看本题:跟上面说的一样,此题相比较上面的理论,唯一不同在于使用的数有限制,数的个数不能超过k,比如第一个样例,不能超过5个,那么23=(2^2)*5+2^1+2^0显然不符合要求,因为2就已经被使用了5次,再思考一个问题,最高位拓展到次高位需要花费的次数是多少呢,是最高位当前被使用过的次数,如果说最高位的使用次数已经超过了当前剩余可使用数的个数k,那么就从最低位开始去利用剩下的可使用数把低位摊分,来保证字典序最大。
#include<bits/stdc++.h>
using namespace std;
#define maxn 100005
#define inf 0x7ffffffffff
typedef long long ll;
int main()
{
ll n,k,m=0;
map<int,int> cnt;
cnt.clear();
cin>>n>>k;
int now;
while(n>0){
cnt[m]=n&1;
n>>=1;
if(cnt[m++]) {k--;now=m-1;}
}
if(k<0) {cout<<"NO"<<endl;return 0;}
while(cnt[now]<=k){
cnt[now-1]+=cnt[now]*2;
k-=cnt[now];
cnt[now]=0;
now--;
}
int cur=min(now,0);
for(cur;cur<m;cur++) if(cnt[cur]) break;
while(k){
if(cnt[cur]) {
cnt[cur-1]+=2;
cnt[cur]--;
k--;
}
cur--;
}
cout<<"Yes"<<endl;
for(int i=m-1;i>=cur;i--){
for(int j=0;j<cnt[i];j++) cout<<i<<" ";
}
return 0;
}