ZOJ 3963 Heap Partition(贪心)

7 篇文章 0 订阅

A sequence S={s1,s2,...,sn} is called heapable if there exists a binary tree T with n nodes such that every node is labelled with exactly one element from the sequence S, and for every non-root node si and its parent sj , sj si and j < i hold. Each element in sequence S can be used to label a node in tree T only once.

Chiaki has a sequence a1,a2,...,an , she would like to decompose it into a minimum number of heapable subsequences.

Note that a subsequence is a sequence that can be derived from another sequence by deleting some elements without changing the order of the remaining elements.

序列 S={s1,s2,...,sn} 被称为 heapable ,当且仅当存在一个二叉树 T , n 个点均作为 T 的一个节点,且满足任意非根节点 si 和其父节点 sj , sjsi j<i

现在有一个序列 a1,a2,...,an ,相应将其分成若干个 heapable 的子序列,问使得子序列个数最少的策略。

解题思路

已知每个树上的节点 sj 均可有最多两个子节点 si ,要求 sjsi 。将所有仍可连接子节点的节点用一个 set 来维护,保证其根据点对应的 ai 从小到大排序。对于一个新的 ai 来说,采用贪心策略将其作为最大的不大于 ai 值的节点的子节点,用 set.upper_bound() 可以在 O(set.size()) 复杂度内实现查找。

需要注意的是,每个节点最多只能有两个子节点,故当某节点已经连接了两个子节点后,应将其从 set 中删除。

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 100000 + 10;
vector<int> v[N];
int n, a[N], cnt, dig[N];
struct Node {
    int first, second;
} p;
bool operator<(Node x, Node y)
{
    if(a[x.second] == a[y.second])    return x.second < y.second;
    return a[x.second] < a[y.second];
}
set<Node > st;
set<Node >::iterator it;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        st.clear();
        cnt = 0;
        scanf("%d",&n);
        memset(dig, 0, 4*n+4);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            p.second = i;
            it = st.upper_bound(p);
            if(it==st.begin())
            {
                v[cnt].push_back(i);
                p.first = cnt,  p.second = i;
                st.insert(p);
                cnt++;
            }
            else
            {
                it--;
                p = *it;
                dig[p.second]++;
                if(dig[p.second] == 2)
                    st.erase(it);
                p.second = i;
                st.insert(p);
                v[p.first].push_back(i);
            }
        }
        printf("%d\n", cnt);
        for(int i=0;i<cnt;i++)
        {
            printf("%d", v[i].size());
            for(int j=0;j<v[i].size();j++)
                printf(" %d", v[i][j]);
            printf("\n");
            v[i].clear();
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值