今天刷 堆
2879 堆的判断
堆是一种常用的数据结构。二叉堆是一个特殊的二叉树,他的父亲节点比两个儿子节点要大,且他的左右子树也是二叉堆。现在输入一颗树(用二叉树的数组表示,即a[i]的左儿子与右儿子分别为a[2i],a[2i+1]),要求判断他是否是一个堆。
一个整数N,表示结点数。
第二行N个整数,表示每个结点代表的数字
如果是,输出‘Yes’
否则输出‘No’
5
1 2 3 4 5
No
1<N<100
数字在2^31以内
1 #include<iostream>
2 #include<cstring>
3 #include<cstdio>
4 using namespace std;
5 int a[110],n;
6 int main()
7 {
8 scanf("%d",&n);
9 for(int i=1;i<=n;i++)
10 scanf("%d",&a[i]);
11 for(int i=1;i<=n;i++)
12 {
13 if(i*2<=n){
14 if(a[i*2]>=a[i]){
15 printf("No\n");return 0;
16 }
17 }
18 if(i*2+1<=n){
19 if(a[i*2+1]>=a[i]){
20 printf("No\n");return 0;
21 }
22 }
23 }
24 printf("Yes\n");
25 return 0;
26 }
3110 二叉堆练习3
时间限制: 3 s 空间限制: 128000 KB 题目等级 : 黄金 Gold
给定N(N≤500,000)和N个整数(较有序),将其排序后输出。
N和N个整数
N个整数(升序)
5
12 11 10 8 9
8 9 10 11 12
对于33%的数据 N≤10000
对于另外33%的数据 N≤100,000 0≤每个数≤1000
对于100%的数据 N≤500,000 0≤每个数≤2*10^9
1 #include<iostream>
2 #include<cstdio>
3 using namespace std;
4 int n,t,a;
5 int heap[500010];
6 void heap_up(int now){
7 if(now<=1) return;
8 int top=now/2;
9 if(heap[now]<heap[top])
10 {
11 swap(heap[now],heap[top]);
12 heap_up(top);
13 }
14 }
15 void heap_down(int now){
16 if(now>n) return;
17 int lc,rc,next=now;
18 bool blc,brc;
19 if((now*2)<=n) blc=true,lc=heap[now*2];
20 else blc=false;
21 if((now*2| 1)<=n) brc=true,rc=heap[now*2| 1];
22 else brc=false;
23 if(blc){
24 if(heap[next]>lc)
25 next=now<<1;
26 }
27 if(brc){
28 if(heap[next]>rc)
29 next=now << 1 | 1;
30 }
31 if(next!=now){
32 swap(heap[next],heap[now]);
33 heap_down(next);
34 }
35 }
36 void heap_pop(){
37 heap[1]=heap[n];
38 n--;
39 heap_down(1);
40 }
41 void make_heap(int x){
42 t++;
43 heap[t]=x;
44 heap_up(t);
45 }
46 int main()
47 {
48 scanf("%d",&n);
49 int m=n;
50 for(int i=1;i<=n;i++)
51 {
52 scanf("%d",&a);
53 make_heap(a);
54 }
55 for(int i=1;i<=m;i++)
56 {
57 printf("%d ",heap[1]);
58 heap_pop();
59 }
60 return 0;
61 }// 手写堆
1 // make_heap
2 #include<bits/stdc++.h>
3 using namespace std;
4 unsigned long long a[1000000+20];
5 int main() {
6 int n;
7 cin >> n;
8 for(int i = 1; i <= n; i++) {
9 scanf("%d", &a[i]);
10 }
11 make_heap(a + 1, a + n + 1);
12 sort_heap(a + 1, a + n + 1);
13 for(int i = 1; i <= n; i++) {
14 printf("%d ", a[i]);
15 }
16 return 0;
17 }
1 // sort
2 #include<iostream>
3 #include<cstring>
4 #include<cstdio>
5 #include<algorithm>
6 using namespace std;
7 int n,a[500010];
8 int main()
9 {
10 scanf("%d",&n);
11 for(int i=1;i<=n;i++)
12 scanf("%d",&a[i]);
13 sort(a+1,a+n+1);
14 for(int i=1;i<=n;i++)
15 printf("%d ",a[i]);
16 return 0;
17 }
时空如图:
三次分别是 make_heap/手敲堆/sort。。。。。
1063 合并果子
2004年NOIP全国联赛普及组
在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。
每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。
因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。
例如有3种果子,数目依次为1,2,9。可以先将1、2堆合并,新堆数目为3,耗费体力为3。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为12,耗费体力为12。所以多多总共耗费体力=3+12=15。可以证明15为最小的体力耗费值。
输入包括两行,第一行是一个整数n(1<=n<=10000),表示果子的种类数。第二行包含n个整数,用空格分隔,第i个整数ai(1<=ai<=20000)是第i种果子的数目。
输出包括一行,这一行只包含一个整数,也就是最小的体力耗费值。输入数据保证这个值小于231。
3
1 2 9
15
对于30%的数据,保证有n<=1000:
对于50%的数据,保证有n<=5000;
对于全部的数据,保证有n<=10000。
1 #include<iostream> 2 #include<cstdio> 3 #include<queue> 4 using namespace std; 5 priority_queue <int> que; 6 int main() 7 { 8 int n,x; 9 scanf("%d",&n); 10 for(int i=1;i<=n;i++) 11 scanf("%d",&x),que.push(-x); 12 int ans=0; 13 for(int i=1,tmp;i<n;i++) 14 { 15 tmp=que.top(); 16 ans-=que.top(); 17 que.pop(); 18 tmp+=que.top(); 19 ans-=que.top(); 20 que.pop(); 21 que.push(tmp); 22 } 23 cout<<ans<<endl; 24 return 0; 25 }
1245 最小的N个和
有两个长度为 N 的序列 A 和 B,在 A 和 B 中各任取一个数可以得到 N^2 个和,求这N^2 个和中最小的 N个。
第一行输入一个正整数N;第二行N个整数Ai 且Ai≤10^9;第三行N个整数Bi,
且Bi≤10^9
输出仅一行,包含 n 个整数,从小到大输出这 N个最小的和,相邻数字之间用
空格隔开。
5
1 3 2 4 5
6 3 4 1 7
2 3 4 4 5
【数据规模】 对于 100%的数据,满足 1≤N≤100000。
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<algorithm> 5 #include<queue> 6 7 #define maxn 100010 8 using namespace std; 9 priority_queue<int,vector<int> > q;// 大头堆 专业建法 10 int n,a[maxn],b[maxn],ans[maxn]; 11 int main() 12 { 13 scanf("%d",&n); 14 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 15 for(int i=1;i<=n;i++) scanf("%d",&b[i]); 16 sort(a+1,a+1+n);sort(b+1,b+1+n); 17 int p=2,cnt=0; 18 for(int i=1;i<=n;i++) q.push(b[1]+a[i]); 19 for(int i=1;i<=n;i++){ 20 while(p<=n&&a[i]+b[p]<q.top()){ 21 q.pop(); 22 q.push(a[i]+b[p]); 23 p++; 24 } 25 p=2; 26 } 27 for(int i=n;i>=1;i--){ 28 ans[i]=q.top(); 29 q.pop(); 30 } 31 for(int i=1;i<=n;i++) printf("%d ",ans[i]); 32 return 0; 33 }
1246 丑数 USACO
对于一给定的素数集合 S = {p1, p2, ..., pK},
来考虑那些质因数全部属于S 的数的集合。这个集合包括,p1, p1p2, p1p1, 和 p1p2p3 (还有其它)。这是个对于一个输入的S的丑数集合。
注意:我们不认为1 是一个丑数。
你的工作是对于输入的集合S去寻找集合中的第N个丑数。longint(signed 32-bit)对于程序是足够的。
第 1 行: 二个被空间分开的整数:K 和 N , 1<= K<=100 , 1<= N<=100,000.
第 2 行: K 个被空间分开的整数:集合S的元素
单独的一行,写上对于输入的S的第N个丑数。
4 19
2 3 5 7
27
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 long long p[105],s[100005]; 6 int k,n; 7 int main() 8 { 9 memset(s,0x7f,sizeof(s)); 10 scanf("%d%d",&k,&n); 11 for(int i=1;i<=k;i++) 12 scanf("%d",&p[i]); 13 s[0]=1; 14 for(int i=1;i<=n;i++){ 15 for(int j=1;j<=k;j++){ 16 int l=0,r=i-1,mid; 17 while(l<r){ 18 mid=(l+r)/2; 19 if(s[mid]*p[j]>s[i-1]) r=mid; 20 else l=mid+1; 21 } 22 s[i]=min(s[i],s[r]*p[j]); 23 } 24 } 25 printf("%d\n",s[n]); 26 return 0; 27 }
3377 [Mz]接水问题2
学校里有一个水房,水房里一共装有m个龙头可供同学们打开水,每个龙头每秒钟的供水量相等,均为1。
现在有n名同学准备接水,他们的初始接水顺序已经确定。将这些同学按接水顺序从 1到n编号,i号同学的接水量为wi。接水开始时,1到m号同学各占一个水龙头,并同时打开水龙头接水。当其中某名同学j完成其接水量要求wj后,下一名排队等候接水的同学k马上接替j同学的位置开始接水。这个换人的过程是瞬间完成的,且没有任何水的浪费。即j同学第x秒结束时完成接水,则k同学第x+1秒立刻开始接水。若当前接水人数n’不足m,则只有n’个龙头供水,其它m-n’个龙头关闭。
现在给出n名同学的接水量,按照上述接水规则,问所有同学都接完水需要多少秒。
特别地,同学们在打水前排好了队,接水所用时间更长的先接。
(ps:出题人本来想设计一个贪心的题目,然后发现用贪心做有反例,只能强改题目,在此声明道歉。不要在意样例解释。)
第 1 行2 个整数 n 和 m,用一个空格隔开,分别表示接水人数和龙头个数。
第 2 行 n 个整数 w1、w2、„„、wn,每两个整数之间用一个空格隔开,wi表示 i 号同学的接水量。
输出只有一行,1 个整数,表示接水所需的总时间。
5 3
4 4 1 2 1
4
【输入输出解释】
第 1 秒,3 人接水。第 1秒结束时,1、2、3 号同学每人的已接水量为 1,3 号同学接完水,4 号同学接替 3 号同学开始接水。
第 2 秒,3 人接水。第 2 秒结束时,1、2 号同学每人的已接水量为 2,4 号同学的已接水量为 1。
第 3 秒,3 人接水。第 3 秒结束时,1、2 号同学每人的已接水量为 3,4 号同学的已接水量为 2。4号同学接完水,5 号同学接替 4 号同学开始接水。
第 4 秒,3 人接水。第 4 秒结束时,1、2 号同学每人的已接水量为 4,5 号同学的已接水量为 1。1、2、5 号同学接完水,即所有人完成接水。
【数据范围】
对于 30%的数据,n≤10,000,m≤1,000;
对于全部的数据,1≤m≤n≤1,000,000,1≤m≤100,000。
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<algorithm> 5 #include<queue> 6 #define maxn 100010 7 using namespace std; 8 struct cmp{ 9 bool operator()(int &a,int &b){ 10 return a>b; 11 } 12 }; 13 priority_queue<int,vector<int>,cmp> q; 14 int n,m,x,a[1000000+2]; 15 int main() 16 { 17 scanf("%d%d",&n,&m); 18 for(int i=1;i<=n;i++) 19 scanf("%d",&a[i]); 20 sort(a+1,a+n+1); 21 for(int i=1;i<=m;i++){ 22 q.push(0); 23 } 24 for(int i=n;i>=1;i--){ 25 int t=q.top();q.pop(); 26 t+=a[i]; 27 q.push(t); 28 } 29 int maxx=0; 30 while(!q.empty()){ 31 maxx=max(maxx,q.top()); 32 q.pop(); 33 } 34 35 printf("%d",maxx); 36 return 0; 37 }
很没意思的一道题~~
2830 蓬莱山辉夜
在幻想乡中,蓬莱山辉夜是月球公主,居住在永远亭上,二次设定说她成天宅在家里玩电脑,亦称NEET姬
一天,她要她帮忙升级月球的网络服务器,应为注册用户过多(月兔和地球上的巫女都注册了……),所以作为代理管理员(俗称网管)的她,非常蛋疼。
注册用户格式:
TouhouMaiden 2004 200
其中前面的Touhoumaiden是预设,不做更改,第一个数是标识,第二个数是每次接受信息访问的间隔用时。
你要做的事,就是给定一群用户及n,求出这n次信息访问中,访问到了谁?
presented by Izayoi sakuya
以题目预设格式输入,另起一行以‘#’结束,在其一行输入n
n行,每行输出第行次后,信息访问到了谁?若在一个时间有若干少女被访问到,输出字典序最小的那位少女的标识
TouhouMaiden 2004 200
TouhouMaiden 2005 300
#
5
2004 2005 2004 2004 2005
标识和每次信息访问间隔均在integer内,n<=10000
原本是要用到堆,但深搜+时间即可搞定
数据有点少但也都够变态了
1 #include<iostream> 2 #include<cstdio> 3 #include<queue> 4 using namespace std; 5 struct kaguya{ 6 int time; 7 int code; 8 int now; 9 } baka,mokou; 10 bool operator<(kaguya a,kaguya b) 11 { 12 if(a.now!=b.now) 13 return a.now>b.now; 14 else 15 return a.code>b.code; 16 } 17 int main() 18 { 19 priority_queue<kaguya> q; 20 int n; 21 string a; 22 while(cin>>a) 23 { 24 if(a=="#")break; 25 cin>>baka.code>>baka.time; 26 baka.now=baka.time; 27 q.push(baka); 28 } 29 cin>>n; 30 while(n--){ 31 mokou=q.top(); 32 q.pop(); 33 cout<<mokou.code<<endl; 34 mokou.now+=mokou.time; 35 q.push(mokou); 36 } 37 return 0; 38 }
基本没读懂题目~~~