ZOJ 3963:Heap Partition(贪心+set+并查集)
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3963
题意:给出一个n个数的序列,可以在其中找一个子序列建堆,并且堆中的父亲结点j和孩子结点i满足sj ≤ si and j < i。问要分配所有的数到堆里面,最少可以建多少个堆。
思路:对于每一个数,如果前面有小于等于它的数并且那个数的左右孩子还没满,那么就可以放在它的下面。考虑最优情况,就应该是每次插入到左右孩子还没满的,并且小于等于当前枚举的数的最大的数,然后插入到这个数后面。
因此可以用set来保存结点,每次都二分查找一下,如果没找到就建新的堆,找到了就插在它后面。
可以用并查集来保存关系。
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define N 100010 4 struct node { 5 int val, id, cnt; 6 bool operator < (const node &rhs) const { 7 if(rhs.val != val) return val < rhs.val; 8 if(rhs.id != id) return id > rhs.id; // 大的id优先,因为val相同的话,要使得二分可以返回val 9 return cnt > rhs.cnt; 10 } 11 }; 12 int fa[N], a[N]; 13 set<node> se; 14 vector<int> ans[N]; 15 16 int Find(int x) { if(x == fa[x]) return x; return fa[x] = Find(fa[x]); } 17 18 int main() { 19 int t; scanf("%d", &t); 20 while(t--) { 21 int n; scanf("%d", &n); 22 se.clear(); int k = 0; 23 for(int i = 1; i <= n; i++) scanf("%d", &a[i]), a[i] = -a[i], fa[i] = i; 24 for(int i = 1; i <= n; i++) { 25 node now = (node) { a[i], i, 2 }; 26 set<node>::iterator it = se.lower_bound(now); 27 // lower_bound返回大于等于now的元素,设成负数,可以变成返回小于等于now的元素 28 if(it != se.end()) { 29 int x = it->val, y = it->id, z = it->cnt; 30 se.erase(it); 31 if(z - 1) se.insert((node) {x, y, z - 1}); 32 fa[i] = Find(y); 33 } 34 se.insert(now); 35 } 36 for(int i = 1; i <= n; i++) if(fa[i] == i) ans[i].clear(), k++; 37 for(int i = 1; i <= n; i++) ans[Find(i)].push_back(i); 38 printf("%d\n", k); 39 for(int i = 1; i <= n; i++) { 40 if(fa[i] != i) continue; 41 printf("%d", ans[i].size()); 42 for(int j = 0; j < ans[i].size(); j++) printf(" %d", ans[i][j]); 43 puts(""); 44 } 45 } 46 return 0; 47 }