题目:
一棵二叉树,根节点是1,往后依次左节点是2*i, 右节点是2* i+1。现从根节点开始往下走,走k个节点,每个节点可取正数或负数,问能否求和凑出n?
1≤N≤109
N≤2k≤260
分析:
因为还要输出路径,和每个数取正还是取负,就感觉应该是个构造或找规律的题,否则不好记录路径。又有
N≤2k
,这句话限制了层数,比如想凑10,1+3+6可以凑出来,只用了3层,但是这是不可以的因为2^3=8,只能用4层。多少可以获得一些启发。
构造方法如下:我们知道前k层的第一个数,
20,21,22...2k
,可以凑出来
2k+1−1
范围内的所有数字。这个数字肯定大于n,现在就让其中某些数字取负,使得和正好等于n。若一个数k取负,那么总和是少了2*k的。所以我们求出差:
sum=(2k+1−1)−n
,因为一个数取负会少2倍,所以那些取负的数字和应该是
d=sum/2
。前k-1层的第一个数可以凑出
2k−1
内的任意数,所以d是肯定能凑出来的。
考虑一个问题,假如sum是偶数,那么毫无问题,直接凑出d即可。
若sum是奇数,那么我们让sum+1变偶数,然后再正常去掉d,但是这样我们去掉了(sum+1),多去了1,怎么办?只需要让最后一个节点走的时候走到右儿子即可。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <algorithm>
using namespace std;
#define ms(a,b) memset(a,b,sizeof(a))
const int inf = 0x3f3f3f3f;
int n, k, sign[65];
int main()
{
int t;
scanf("%d", &t);
for(int tt=1;tt<=t;tt++){
ms(sign, 0); // 0是+
scanf("%d%d", &n, &k);
long long tmp = ((long long)1 << k)-1-n;
int flag = 0;
if(tmp&1){
tmp++;
flag = 1;
}
for(int i=1;i<=k;i++){
sign[i] = (tmp>>i)&1;
}
printf("Case #%d:\n", tt);
for(int i=1;i<=k-1;i++){
printf("%d %c\n", (1<<(i-1)), sign[i] ? '-':'+');
}
if(!flag){
printf("%d +\n", (1<<(k-1)));
}
else{
printf("%d +\n", (1<<(k-1))+1);
}
}
return 0;
}