偷懒的桐桐

偷懒的桐桐

题目描述

桐桐的老师布置桐桐写一个小根堆,但是桐桐不会堆的操作,所以想了一个偷懒的办法:
堆是一棵完全二叉树,每个结点有一个权。小根堆的根的权最小,且根的两个子树也是一个堆。可以用一个数组a来记录一棵完全二叉树,a[1]为根结点,若结点a[j]不是根结点,那么它的父亲为a[j div 2];若结点a[k]不是叶子结点,那么它的左儿子为a[2k],它的右儿子为a[2k+1]。
  桐桐希望一组数据按一定顺序依次插入数组中(即第i个数为a[i]),最后得出来就已经是一个堆,即不需要任何交换操作,若有多种方法,输出字典序最大的一组,使得这个数据更乱。
  

输入

输入的第1行为一个正整数n,为插入的数字的个数;
第2行包含n个正整数,为所有需要插入的数字,数字之间用空格隔开。
  为了简化题目,数据保证n=2k-1,即保证最后的堆是一棵满二叉树。
 

输出

输出包括1行,为插入的序列,数字之间用空格隔开,行末换行并没有空格。

样例输入

3
10 2 1

样例输出

1 10 2

提示

样例说明 1 2 10 与1 10 2都是满足要求的插入序列,但是1 10 2的字典序更大。

20%的数据:n≤7;
30%的数据:n≤15;
50%的数据:n≤1023;
100%的数:n≤65535,所有数字不超过108,且各不相同。

分析:构造字典序最大的二叉树;

   预处理排序,第一个肯定是根;

   接下来根据贪心原则,剩下的分成2半,第二半的第一个既是根的第一个儿子,第一半的第一个既是根的第二个儿子;

   每一半继续递归即可;

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <climits>
#include <cstring>
#include <string>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <vector>
#include <list>
#include <bitset>
#define rep(i,m,n) for(i=m;i<=n;i++)
#define rsp(it,s) for(set<int>::iterator it=s.begin();it!=s.end();it++)
#define vi vector<int>
#define pii pair<int,int>
#define mod 1000000007
#define inf 0x3f3f3f3f
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define ll long long
#define pi acos(-1.0)
const int maxn=1e6+10;
const int dis[4][2]={{0,1},{-1,0},{0,-1},{1,0}};
using namespace std;
ll gcd(ll p,ll q){return q==0?p:gcd(q,p%q);}
ll qpow(ll p,ll q){ll f=1;while(q){if(q&1)f=f*p;p=p*p;q>>=1;}return f;}
int n,m,a[maxn],ans[maxn];
void dfs(int now,int l,int r)
{
    ans[now]=a[l];
    if(l==r)return;
    int len=(r-l)/2;
    dfs(now*2+1,l+len+1,l+2*len);
    dfs(now*2+2,l+1,l+len);
}
int main()
{
    int i,j,k,t;
    scanf("%d",&n);
    rep(i,0,n-1)scanf("%d",&a[i]);
    sort(a,a+n);
    dfs(0,0,n-1);
    printf("%d",ans[0]);
    rep(i,1,n-1)printf(" %d",ans[i]);
    printf("
");
    //system ("pause");
    return 0;
}
原文地址:https://www.cnblogs.com/dyzll/p/5743059.html