zoukankan      html  css  js  c++  java
  • 「数据结构」第1章 二叉堆课堂过关

    「数据结构」第1章 二叉堆课堂过关

    堆-结构体模板

    struct Heap {
    	#define max_(_ , __) ((_) < (__) ? (_) : (__))
    	int siz;
    	int a[nn * 2];
    	bool ty;
    	inline void swap_(int x , int y) {int tmp = a[x] ; a[x] = a[y] ; a[y] = tmp;}
    	void clear() {
    		siz = 0 , ty = 0;
    		memset(a , 0 , sizeof(a));
    	}
    	inline void push(int dat) {
    		a[++siz] = dat;
    		int p = siz;
    		while(p > 1 && (!(a[p >> 1] < a[p]) ^ ty))
    			swap_(p >> 1 , p) , p >>= 1;
    	}
    	inline void pop() {
    		swap_(1 , siz);	--siz;
    		int p = 1 , tmp;
    		//这里可读性比较差,注意向下调节时当前节点与两个子节点比较
    		while(p * 2 <= siz && (!(a[p] < a[tmp = ( p * 2 + 1 > siz ? p * 2 : (a[p * 2] < a[p * 2 + 1] ^ ty ? p * 2 : p * 2 + 1) ) ] ) ^ ty)) 
    			swap_(tmp , p) , p = tmp;
    	}
    	inline int top() {
    		return siz == 0 ? 0 : a[1];
    	}
    	inline bool empty(){return siz == 0;}
    }h;
    

    A. 【例题1】合并果子

    题目

    代码

    手工堆

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #define nn 100010
    using namespace std;
    int read() {
    	int re = 0;
    	char c = getchar();
    	while(c < '0' || c > '9') c = getchar();
    	while(c >= '0' && c <= '9')
    		re = (re << 1) + (re << 3) + c - '0',
    		c = getchar();
    	return re;
    }
    struct Heap {
    	#define max_(_ , __) ((_) < (__) ? (_) : (__))
    	int siz;
    	int a[nn * 2];
    	bool ty;
    	inline void swap_(int x , int y) {int tmp = a[x] ; a[x] = a[y] ; a[y] = tmp;}
    	void clear() {
    		siz = 0 , ty = 0;
    		memset(a , 0 , sizeof(a));
    	}
    	inline void push(int dat) {
    		a[++siz] = dat;
    		int p = siz;
    		while(p > 1 && (!(a[p >> 1] < a[p]) ^ ty))
    			swap_(p >> 1 , p) , p >>= 1;
    	}
    	inline void pop() {
    		swap_(1 , siz);	--siz;
    		int p = 1 , tmp;
    		//这里可读性比较差,注意向下调节时当前节点与两个子节点比较
    		while(p * 2 <= siz && (!(a[p] < a[tmp = ( p * 2 + 1 > siz ? p * 2 : (a[p * 2] < a[p * 2 + 1] ^ ty ? p * 2 : p * 2 + 1) ) ] ) ^ ty)) 
    			swap_(tmp , p) , p = tmp;
    	}
    	inline int top() {
    		return siz == 0 ? 0 : a[1];
    	}
    	inline bool empty(){return siz == 0;}
    }h;
    int n;
    int main() {
    	h.clear();
    	n = read();
    	for(int i = 1 ; i <= n ; i++)
    		h.push(read());
    	int ans = 0;
    	while(h.siz > 1) {
    		int add = 0;
    		add += h.top();	h.pop();
    		add += h.top();	h.pop();
    		ans += add;
    		h.push(add);
    	}
    	cout << ans;
    	return 0;
    }
    

    STL优先队列

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    #define nn 100010
    
    using namespace std;
    int read() {
    	int re = 0;
    	char c = getchar();
    	while(c < '0' || c > '9') c = getchar();
    	while(c >= '0' && c <= '9')
    		re = (re << 1) + (re << 3) + c - '0',
    		c = getchar();
    	return re;
    }
    priority_queue <int> h;
    int n;
    int main() {
    	n = read();
    	for(int i = 1 ; i <= n ; i++)
    		h.push(-read());//默认大根堆
    	int ans = 0;
    	while(h.size() > 1) {
    		int add = 0;
    		add += h.top();	h.pop();
    		add += h.top();	h.pop();
    		ans += add;
    		h.push(add);
    	}
    	cout << -ans;
    	return 0;
    }
    

    B. 【例题2】序列合并

    题目

    思路

    (b)数组从小到大排序的

    显然,对于同一个(i),若(j)越大,(a_i+b_j)越大.题目要求的是前(n)小,所以我们设一个(p)数组,初始值全部为1,将(a_i+b_{p_i}(1le ile n))(i)捆绑起来压入小根堆,进行(n)次循环,每次从堆中取出最小的(a_i+b_{p_i}),(p_i=p_i+1),若(p_ile n),将(a_i+b_{p_i})压入小根堆(注意(p_i))已发生变化

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define nn 100010
    using namespace std;
    int read() {
    	int re = 0;
    	char c = getchar();
    	while(c < '0' || c > '9') c = getchar();
    	while(c >= '0' && c <= '9')
    		re = (re << 1) + (re << 3) + c - '0',
    		c = getchar();
    	return re;
    }
    struct Heap {
    	#define max_(_ , __) ((_) < (__) ? (_) : (__))
    	int siz;
    	int a[nn * 2];
    	int dat[nn * 2];
    	
    	bool ty;
    	inline void swap_(int x , int y) {int tmp = a[x] ; a[x] = a[y] ; a[y] = tmp;	tmp = dat[x] ; dat[x] = dat[y] ; dat[y] = tmp;}
    	void clear() {
    		siz = 0 , ty = 0;
    		memset(a , 0 , sizeof(a));
    		memset(dat , 0 , sizeof(dat));
    	}
    	inline void push(int a_ , int dat_) {
    		a[++siz] = a_;
    		dat[siz] = dat_;
    		int p = siz;
    		while(p > 1 && (!(a[p >> 1] < a[p]) ^ ty))
    			swap_(p >> 1 , p) , p >>= 1;
    	}
    	inline void pop() {
    		swap_(1 , siz);	--siz;
    		int p = 1 , tmp;
    		while(p * 2 <= siz && (!(a[p] < a[tmp = ( p * 2 + 1 > siz ? p * 2 : (a[p * 2] < a[p * 2 + 1] ^ ty ? p * 2 : p * 2 + 1) ) ] ) ^ ty)) 
    			swap_(tmp , p) , p = tmp;
    	}
    	inline int top() {
    		return siz == 0 ? 0 : a[1];
    	}
    	inline int top_dat() {
    		return siz == 0 ? 0 : dat[1];
    	}
    	inline bool empty(){return siz == 0;}
    }h;
    int n;
    int a[nn];
    int b[nn];
    int p[nn];
    int main() {
    	h.clear();
    	n = read();
    	for(int i = 1 ; i <= n ; i++)
    		a[i] = read();
    	for(int i = 1 ; i <= n ; i++)
    		b[i] = read();
    	
    	
    	for(int i = 1 ; i <= n ; i++) {
    		p[i] = 1;
    		h.push(a[i] + b[p[i]] , i);
    	}
    	
    	for(int i = 1 ; i <= n ; i++) {
    		printf("%d " , h.top());
    		int k = h.top_dat();
    		h.pop();
    		p[k]++;
    		if(p[k] <= n)
    			h.push(a[k] + b[p[k]] , k);
    	}
    	return 0;
    }
    

    C. 【例题3】龙珠游戏

    题目

    传送门

    思路

    首先读懂题目:输入的是一个排列

    其次,这题和堆好像一点关系都没有

    我的想法

    理论复杂度(O(nsqrt n)),其实从对拍来看,当(nle10^5)时,速度和洛谷上(O(n))标程相当.

    几个数组:

    int a[nn];//输入的数组
    int p[nn];//p[i]表示数字i在a中的下标
    int nxt[nn];//优化,nxt[i]=j表示区间(i,j)中所有vis值均为true
    bool vis[nn];//若vis[i]==true,则a[i]已经加入到目标队列
    

    不难想到,(i)(n)往1找,若(vis_{p_i}==false),从(p_i)开始找第一个(j),使(vis_j==false)(若没找到,证明(i)是原龙珠序列的最后一个数),更新(vis),输出(i),(a_j)

    时间复杂度可以去到(O(n^2)),需要优化

    可以发现,不必要的时间用在找(j),因此,我们从这里入手

    借助分块思想,如果找(j)的循环次数超过(sqrt n),我们就更新(nxt)数组:

    			int las = n + 1;
    			for(int j = n ; j > 0 ; j--)
    				if(!vis[j])
    					nxt[j] = las , las = j;
    

    这样,找(j)的循环次数基本不超过(sqrt n),更新(nxt)数组的次数也不超过(sqrt n),每次更新需要(O(n))的时间

    所以,时间复杂度为(O(nsqrt n)),可通过

    来自洛谷某题解

    新鲜的题!!!

    先用链表存储每一个编号的数前一个和后一个数的编号,最后由大到小枚举一遍,将没有用过的点连上后一个一起输出(注意它能输出,当且仅当它后面有数)

    输出以后记得把它前一个数和它后面的后面的数连上

    然后,就可以上代码了

    #include <cstdio>
    
    int n,a[100001],k[100001],x[100001],i;
    
    int main()
    {
     scanf("%d",&n);
     for(i=1;i<=n;++i){
         scanf("%d",&a[i]);
         k[a[i-1]]=a[i];
         x[a[i]]=a[i-1];}
     for(i=n;i>=1;--i)
         if(k[i])printf("%d %d ",i,k[i]),k[x[i]]=k[k[i]],x[k[x[i]]]=x[i],k[k[i]]=0;
     return 0;
    }
    

    代码

    AC代码

    #include <iostream>
    #include <cstdio>
    #define nn 100010
    using namespace std;
    int read() {
    	int re = 0;
    	char c = getchar();
    	while(c < '0' || c > '9') c = getchar();
    	while(c >= '0' && c <= '9')
    		re = (re << 1) + (re << 3) + c - '0',
    		c = getchar();
    	return re;
    }
    int n;
    int a[nn];
    int p[nn];
    int nxt[nn];
    bool vis[nn];
    int main() {
    	n = read();
    	for(int i = 1 ; i <= n ; i++)
    		p[a[i] = read()] = i;
    	for(int i = 1 ; i <= n + 1 ; i++)
    		nxt[i] = i + 1;
    		
    	for(int i = n ; i > 0 ; i--) {
    		if(vis[p[i]])	continue;
    		int curnxt = nxt[p[i]];
    		int cnt = 0;
    		while(vis[curnxt] && curnxt <= n)
    			curnxt = nxt[curnxt] , ++cnt;
    		if(curnxt == n + 1)
    			continue;
    		printf("%d %d " , i , a[curnxt]);
    		vis[curnxt] = vis[p[i]] = true;
    		
    		if(cnt * cnt >= n) {
    			int las = n + 1;
    			for(int j = n ; j > 0 ; j--)
    				if(!vis[j])
    					nxt[j] = las , las = j;
    		}
    	}
    	return 0;
    }
    

    随机数据

    #include <bits/stdc++.h>
    using namespace std;
    int a[100010];
    int main() {
    	unsigned seed;
    	cin >> seed;
    	seed *= time(0);
    	srand(seed);
    	
    	int n = (long long)seed * rand() * rand() % 99999 + 1;
    	if(n & 1)++n;
    	cout << n << endl;
    	for(int i = 1 ; i <= n ; i++)
    		a[i] = i;
    	random_shuffle(a + 1 , a + n + 1);
    	for(int i = 1 ; i <= n ; i++)
    		printf("%d " , a[i]);
    	return 0;
    }
    

    D. 【例题4】工作安排

    题目

    传送门

    思路

    贪心+大根堆

    我们把工作截止时间从大到小排序,设当前时间为(d_i)(注意此时第(i)项工作已经截止),把截止时间为(d_i)的工作全部放进堆里,设(j<i)(d_j eq d_i),(j)取最大值,我们若堆不为空,从堆中取出(d_j-d_i)个工作,把它们的(P)累加到答案中

    输出答案即可

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define nn 100010
    #define ll long long
    using namespace std;
    int read() {
    	int re = 0;
    	char c = getchar();
    	while(c < '0' || c > '9') c = getchar();
    	while(c >= '0' && c <= '9')
    		re = (re << 1) + (re << 3) + c - '0',
    		c = getchar();
    	return re;
    }
    struct Heap {
    	int siz;
    	int a[nn * 2] , b[nn * 2];
    	bool ty;
    	inline void swap_(int x , int y) {
    		int tmp;
    		tmp = a[x] ; a[x] = a[y] ; a[y] = tmp;
    		tmp = b[x] ; b[x] = b[y] ; b[y] = tmp;
    	}
    	void clear() {
    		siz = 0 , ty = 0;
    		memset(a , 0 , sizeof(a));
    	}
    	inline void push(int dat , int dat2) {
    		a[++siz] = dat;
    		b[siz] = dat2;
    		int p = siz;
    		while(p > 1 && (!(a[p >> 1] < a[p]) ^ ty))
    			swap_(p >> 1 , p) , p >>= 1;
    	}
    	inline void pop() {
    		swap_(1 , siz);
    		--siz;
    		int p = 1 , tmp;
    		while(p * 2 <= siz && (!(a[p] < a[tmp = ( p * 2 + 1 > siz ? p * 2 : (a[p * 2] < a[p * 2 + 1] ^ ty ? p * 2 : p * 2 + 1) ) ] ) ^ ty))
    			swap_(tmp , p) , p = tmp;
    	}
    	inline int top() {
    		return siz == 0 ? 0 : a[1];
    	}
    	inline bool empty() {
    		return siz == 0;
    	}
    } h;
    struct node {
    	int d , p;
    } wk[nn];
    bool cmp(node a , node b) {
    	return a.d > b.d;
    };
    int n;
    signed main() {
    //	freopen("P2949_2.in" , "r" , stdin);
    	h.clear();
    	h.ty = 1;
    
    	n = read();
    	for(int i = 1 ; i <= n ; i++)
    		wk[i].d = read() , wk[i].p = read();
    	++n;
    	wk[n].d = wk[n].p = 0;
    	sort(wk + 1 , wk + n + 1 , cmp);
    	
    	ll ans = 0;
    	h.push(wk[1].p , wk[1].d);
    	
    	int i;
    	for(i = 2 ; i <= n && wk[i].d == wk[i - 1].d ; i++)
    		h.push(wk[i].p , wk[i].d);
    	while(i <= n) {
    		int num = wk[i - 1].d - wk[i].d;
    		while(num-- && !h.empty()) {
    			ans += (ll)h.top();
    			h.pop();
    		}
    		h.push(wk[i].p , wk[i].d);
    		for(++i ; i <= n && wk[i].d == wk[i - 1].d ; i++)
    			h.push(wk[i].p , wk[i].d);
    	}
    	cout << ans;
    	return 0;
    }
    
  • 相关阅读:
    (Good Bye 2019) Codeforces 1270B Interesting Subarray
    (Good Bye 2019) Codeforces 1270A Card Game
    Codeforces 1283D Christmas Trees(BFS)
    Codeforces 1283C Friends and Gifts
    Codeforces 1283B Candies Division
    1095 Cars on Campus (30)
    1080 Graduate Admission (30)
    1099 Build A Binary Search Tree (30)
    1018 Public Bike Management (30)
    1087 All Roads Lead to Rome (30)
  • 原文地址:https://www.cnblogs.com/dream1024/p/14634230.html
Copyright © 2011-2022 走看看