• 「数据结构」第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;
    }
    
  • 相关阅读:
    军火库(第一期):无线电硬件安全大牛都用哪些利器?
    AMD64与IA64的区别
    win7安装apache或者php 5.7缺少vcruntime140.dll的问题
    DrawText
    Delphi与C语言类型转换对照
    chm文件打开空白无内容的解决办法
    在ubuntu 15.04下安装VMware Tools
    Vmware怎样使用nat和桥接方式解决虚拟机联网问题
    Ubuntu 14.04/14.10下安装VMware Workstation 11图文教程
    Ubuntu 16.04 安装 VMware-Workstation-12
  • 原文地址:https://www.cnblogs.com/dream1024/p/14634230.html
Copyright © 2020-2023  润新知