ZOJ - 3963 (贪心) ACM-2017-浙江省赛

ZOJ - 3963 ACM-2017-浙江省赛

题目链接:ZOJ-3963.

题目描述

给出一个长度为 n 的整数序列 { S1, S2, … , Sn} ,用这些数组成二叉树,要求满足:父亲节点的值大于等于其儿子节点的值,且下标小于儿子节点的下标。
问:放下所有的点,最少需要多少棵二叉树。

思路

错误思路
找下降子序列,认为只有找到比当前所有二叉树头结点最小的点时,才需要增加一课二叉树。
反例: 1 7 8 9 4 5 6 2 3
错误点:忽略了下标因素
正确思路
从前往后依次遍历每个点,对于每个待插入的点 x ,在它之前的所有节点中,找到比它小且最大的那一个点 fx ,且 fx 没有儿子节点或只有一个儿子节点,将 x 放在 fx 儿子节点的位置。如果没有满足的 fx,则以 x 为头结点新建一棵二叉树。
具体实现:用 set 存放:已经连接至二叉树上,出度小于2(少于两个儿子节点)的点。对于每个待插入的点,若能找到 fx,将 x 放入 fx 的二叉树中,更新 fx 的出度,如果出度等于2,从set中删除该点。点的值可能会重复,可记录set中本应该有的该点的次数。

代码


#include <bits/stdc++.h>

using namespace std;
#define MAXN 1000005
set<int>s;
vector<int>v[MAXN];//记录每个二叉树 
int c[MAXN], Out[MAXN], to[MAXN];//Out[x]记录 x 的出度,to[x]表示 x 所属二叉树 
int main() {
	set<int>::iterator it;
	int T, n, x;
	scanf("%d", &T);
	while(T--) {
		int num = 0;
		scanf("%d", &n);
		s.clear();
		for(int i = 1; i <= n; i++) {
			c[i] = 0;
			Out[i] = 0;
			to[i] = i;
			v[i].clear();
		}
		for(int i = 1; i <= n; i++) {
			scanf("%d", &x);
			if(i == 1) {
				num = 1;
				s.insert(x);
				c[x]++;
				v[num].push_back(i);
				to[x] = num;
			}
			else {
				it = s.upper_bound(x);
				if(it == s.begin()) {
					num++;
					v[num].push_back(i);
					s.insert(x);
					c[x]++;
					to[x] = num;
				}
				else{
					it--;
					int fx = *it;
					Out[fx]++;
					if(Out[fx] == 2) {
						if(c[fx] > 1) {
							c[fx]--;
							Out[fx] = 0;
						}
						else {
							s.erase(fx);
							c[fx] = 0;
							Out[fx] = 0; // WA 
						}
					}
					if(!s.count(x)) {
						s.insert(x);
					}
					c[x]++;
					to[x] = to[fx];
					v[ to[fx] ].push_back(i);					
				}
			}
		}
		printf("%d\n", num);
		for(int i = 1; i <= num; i++) {
			int z = v[i].size();
			printf("%d ",z);
			for(int j = 0; j < z; j++) {
				printf("%d", v[i][j]);
				if(j != z - 1) printf(" ");
			}
			printf("\n");
		}
	}
	return 0;
}

错误点

在删除出度为2的节点时,没有将其出度清零,导致后续出度为3。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值