CF1095C Powers Of Two(二进制拆分/STL优先队列)

题目描述

赵老师做为一个程序员,实在是太喜欢二进制了
以至于他十分喜欢2的次幂
他想要让你帮他算出正整数n是否可以被分成k个2的某次幂之和
Input
第一行只有两个正整数n,k (1 ≤ n ≤ 10^9 , 1 ≤ k ≤ 2· 10^5).

Output
如果不存在这样的方案,输出NO.

否则输出YES,并且输出 k个正整数,它们的和是n,如果有多种方案,输出任意一种.

Examples
Input
9 4
Output
YES
1 2 2 4
Input
8 1
Output
YES
8
Input
5 1
Output
NO
Input
3 7
Output
NO

题目分析:

拿n=5,k=3举例子,拆分成二进制为(101),此时有两个1,表示可以拆成至少两份 为 4+1,但是k=3,

根据性质 2n = 2n-1 * 2

我们可以将二进制(101)从最高位开始把 1 传递到下一位 (021) 这样 拆分成了 2+ 2 +1 三份,

我们可以使用一个从大到小排列的优先队列把4和1放入队列中,依次取出最高位然后 把两个最高位除以2 重新放入队列中,知道满足sum=k;

ACcode


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <iomanip>
#define  gold_m main
using namespace std;
typedef long long ll;
const int N=1e6+10;
ll a[N];
int gold_m() {

	ll n,k;

	cin>>n>>k;
	if(n==0&&k==0||k>n) {
		cout<<"NO";
		return 0;
	}
	ll cnt=1,sum=0;
	priority_queue<ll,vector<ll>,less<ll>>heap;
	while(n>0) {
		if(n%2)
			heap.push(n%2*cnt);
		if(n%2) sum++;
		cnt*=2;
		n/=2;
	}
	if(sum>k) {
		cout<<"NO";
		return 0;
	}
	cout<<"YES"<<endl;
	while(heap.size()<k) {
		ll temp=heap.top();
		heap.pop();
		if(temp>=2) {
			heap.push(temp/2);
			heap.push(temp/2);
		}
	}
	while(!heap.empty()) {
		cout<<heap.top()<<" ";
		heap.pop();
	}


	return 0;
}

另一种不使用STL的写法

同样的思路

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <iomanip>
#define  gold_m main
using namespace std;
typedef long long ll;
const int N=1e6+10;
ll a[N];
int gold_m() {
	ll n,k;
	cin>>n>>k;
	if(n==0&&k==0||k>n) {
		cout<<"NO";
		return 0;
	}
	ll cnt=0,sum=0;
	while(n>0) {
		a[++cnt]=n%2;
		if(n%2) sum++;
		n/=2;
	}
	ll t= cnt;
	// 从高位cnt开始
	while(sum<k) {
		if(cnt==1)
			break;
		if(a[cnt]) {
			a[cnt]--;
			a[cnt-1]+=2;
		} else {
			cnt--;
		}
		if(a[cnt])
			sum++;
	}

	if(sum!=k) {
		cout<<"NO";
		return 0;
	}
	cout<<"YES"<<endl;
	ll temp=1;
	for(int i=1 ; i<=t; i++) {
		for(int j=1; j<=a[i]; j++)
			cout<<temp<<" ";
		temp*=2;
	}


	return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值