HDU 5573 Binary Tree

参看资料:

https://blog.csdn.net/u013068502/article/details/50094561


题目: 

The Old Frog King lives on the root of an infinite tree. According to the law, each node should connect to exactly two nodes on the next level, forming a full binary tree. 

Since the king is professional in math, he sets a number to each node. Specifically, the root of the tree, where the King lives, is 11. Say froot=1froot=1. 

And for each node uu, labels as fufu, the left child is fu×2fu×2 and right child is fu×2+1fu×2+1. The king looks at his tree kingdom, and feels satisfied. 

Time flies, and the frog king gets sick. According to the old dark magic, there is a way for the king to live for another NN years, only if he could collect exactly NNsoul gems. 

Initially the king has zero soul gems, and he is now at the root. He will walk down, choosing left or right child to continue. Each time at node xx, the number at the node is fxfx (remember froot=1froot=1), he can choose to increase his number of soul gem by fxfx, or decrease it by fxfx. 

He will walk from the root, visit exactly KK nodes (including the root), and do the increasement or decreasement as told. If at last the number is NN, then he will succeed. 

Noting as the soul gem is some kind of magic, the number of soul gems the king has could be negative. 

Given NN, KK, help the King find a way to collect exactly NN soul gems by visiting exactly KK nodes.

Input

First line contains an integer TT, which indicates the number of test cases. 

Every test case contains two integers NN and KK, which indicates soul gems the frog king want to collect and number of nodes he can visit. 

⋅⋅ 1≤T≤1001≤T≤100. 

⋅⋅ 1≤N≤1091≤N≤109. 

⋅⋅ N≤2K≤260N≤2K≤260.

Output

For every test case, you should output " Case #x:" first, where xx indicates the case number and counts from 11. 

Then KK lines follows, each line is formated as 'a b', where aa is node label of the node the frog visited, and bb is either '+' or '-' which means he increases / decreases his number by aa. 

It's guaranteed that there are at least one solution and if there are more than one solutions, you can output any of them. 

Sample Input

2
5 3
10 4

Sample Output

Case #1:
1 +
3 -
7 +
Case #2:
1 +
3 +
6 -
12 +

 题目大意:

       有一棵满二叉树,根结点值为1,左子结点为2∗i2∗i,右子结点为2∗i+12∗i+1,给出n和k,求在二叉树上从根向下共走k步,每步可以为当前结点权值取正号或者负号,求一种使最终取值和为n的可行方案。

解题思路:

       这道题完全没思路,一开始不知道可以将值取为负值,以为是用深搜,又不知道终点条件是什么。

       看了看题解,大家都是一个思路,二进制表示的反向应用,,关键是我对二进制最晕了,,细看了几遍,明白了。

       先想我们将一个整数用二进制表示,例如:10,二进制为1010,那么用2的幂表示即为 2^1+2^3。yes,所有的正整数都可以用2的幂的和唯一表示。2的幂次方,即我们这棵完全二叉树的所有最左边的边。

       但是,我们如果只依靠最左边这一列的加减法,只能表示所有的奇数,来思考一下,1,2,4,8,16,,,从1开始加或者减:<1> 1+偶=奇数;<2> 1-偶=奇数;<3> -1+偶=奇数;<4> -1-偶=奇数;

       所以,如果想表示偶数,只需要将最后一位从左子树换为右子树即可;

       则对于任意一个正整数,只需要将其二进制上为1的全部取正数,为0的全部不取即可;但是我们的每一个子节点不允许空手而过,必须对每一个节点取正或者取负,那该怎么办,把我不应该取的取负,整体就变小了,把我不应该取的取正,整体就变大了,那就让他们自相残杀,刚好最终等于0就好了【是指那些本不应该取的二进制位上对应的值】;

       即,将所有的最左边的节点全部相加,取和,找出比要取的值多出了多少,除以2,将这“一半”的二进制表示对应的点取负,即用所有节点的和里面的剩余另一半减去,得到最终结果。【偶数将总和先加1变成奇数,即最后的节点换为右节点

       例:

              k=4的话,sum=1+2+4+8=2^4-1=15。

              n=3:d=sum-n=12,x=d/2=12/2=6,那么我们用2^0,2^1,2^2,..,2^(k-2)构造出6来就可以了,即选2和4,那么把2和4取负,其他的全正即可:3=1-2-4+8。

              n=6:此时按上面的方法算出来的d=sum-n肯定不是偶数,为了能得到偶数,则sum应该变成sum=1+2+4+9=16.(即在最后一步向右,不走8,而走9),此时d=sum-n=16-6=10,

              x=d/2=10/2=5.那么我们用2^0,2^1,2^2,..,2^(k-2)构造出5来就可以了,即选1和4,那么把1和4取负,其他的全正即可:6=-1+2-4+9。

              至于为什么一定要保证d是偶数相信大家都能想明白。

实现代码:

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long LL;

LL fact[65],bit[65],res[65];
int sign[65];
int main(){
    LL i,j,n,k,T,kcase=1;
    fact[1]=1;
    for(i=2;i<=61;i++)  //存储最左边节点的值
        fact[i]=fact[i-1]*2;
    scanf("%I64d",&T);
    while(T--){
        scanf("%I64d%I64d",&n,&k);
        for(i=1;i<=k;i++){
            res[i]=fact[i];
            sign[i]=1;
        }
        LL d=fact[k+1]-1-n;
        if(n%2==0){ //如果所求值为偶数,+1;
            res[k]++;d++;
        }
        LL x=d/2,cnt=0;
        while(x){   //将多出的值的一半用二进制表示
            bit[cnt++]=x%2;
            x/=2;
        }
        for(i=1,j=0;j<cnt;j++){     //用0表示该值可以取
            if(bit[j]) sign[i]=0;
            i++;
        }
        printf("Case #%I64d:\n",kcase++);
        for(i=1;i<=k;i++){
            printf("%I64d ",res[i]);
            if(sign[i])     //0,可取
                printf("+\n");
            else printf("-\n");
        }
    }
    return 0;
}

~

#include<bits/stdc++.h>
using namespace std;
int p[61];
void init(){
    p[0]=1;
    for(int i=1;i<=62;i++){
        p[i]=p[i-1]<<1;
    }
}

int main(){
    init();

    int t,n,k,sum,num=1;
    scanf("%d",&t);

    while(t--){
        sum=0;
        int d,dnum=0,ds[33];
        memset(ds,0,sizeof(ds));
        scanf("%d%d",&n,&k);
        if(n%2==0){sum+=1;}
        for(int i=0;i<k;i++)
            sum+=p[i];

        d=(sum-n)/2;
        while(d>0){
            ds[dnum++]=d%2;
            d/=2;
        }
        printf("Case #%d:\n",num++);

        for(int i=0;i<k;i++){
            if(i==k-1&&n%2==0)
                 printf("%d ",p[i]+1);
            else printf("%d ",p[i]);
            
            if(ds[i]==1) printf("-\n");
            else printf("+\n");
        }
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容,这是一道关于二叉树的问题,需要输出每个二叉树的层序遍历。如果二叉树没有完全给出,则输出“not complete”。而且,这道题目是一个ACM竞赛的题目,需要使用Java语言进行编写。 以下是Java语言的代码实现: ```java import java.util.LinkedList; import java.util.Queue; import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); while (sc.hasNext()) { String s = sc.nextLine(); if (s.equals("")) { continue; } String[] arr = s.split(" "); int len = arr.length; int[] tree = new int[len]; boolean[] flag = new boolean[len]; for (int i = 0; i < len; i++) { if (arr[i].equals("()")) { tree[i] = -1; flag[i] = true; } else { tree[i] = Integer.parseInt(arr[i].substring(1, arr[i].length() - 1)); } } int root = 0; for (int i = 0; i < len; i++) { if (!flag[i]) { root = i; break; } } boolean isComplete = true; Queue<Integer> queue = new LinkedList<>(); queue.offer(root); int index = 1; while (!queue.isEmpty()) { int size = queue.size(); for (int i = 0; i < size; i++) { int cur = queue.poll(); if (tree[cur] == -1) { if (index < len && !flag[index]) { isComplete = false; } } else { if (cur * 2 + 1 < len) { queue.offer(cur * 2 + 1); if (tree[cur * 2 + 1] != -1) { flag[cur * 2 + 1] = true; } } if (cur * 2 + 2 < len) { queue.offer(cur * 2 + 2); if (tree[cur * 2 + 2] != -1) { flag[cur * 2 + 2] = true; } } } index++; } } if (!isComplete) { System.out.println("not complete"); continue; } queue.offer(root); StringBuilder sb = new StringBuilder(); while (!queue.isEmpty()) { int size = queue.size(); for (int i = 0; i < size; i++) { int cur = queue.poll(); sb.append(tree[cur]).append(" "); if (cur * 2 + 1 < len && tree[cur * 2 + 1] != -1) { queue.offer(cur * 2 + 1); } if (cur * 2 + 2 < len && tree[cur * 2 + 2] != -1) { queue.offer(cur * 2 + 2); } } } System.out.println(sb.toString().trim()); } } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值