堆
- 一些注意点:
左儿子的编号是自己的编号*2+1
右儿子的编号是自己的编号*2+2
父亲节点的编号是(自己的编号-1)/2 - 手动实现的堆,贴一段书上的代码:
1 #include <iostream> 2 3 using namespace std; 4 5 int const MAX_N=233333; 6 int heap[MAX_N]; 7 int sz,n; 8 9 void push(int); 10 int pop(void); 11 12 int main() 13 { 14 cin >> n; 15 for (int i=0; i<n; i++) 16 { 17 int x; 18 cin >> x; 19 push(x); 20 } 21 for (int i=0; i<sz; i++) cout << heap[i] << " "; 22 cout << endl; 23 n=sz; 24 for (int i=0; i<n/2; i++) 25 cout << "pop: " << pop() << endl; 26 for (int i=0; i<sz; i++) cout << heap[i] << " "; 27 cout << endl; 28 } 29 30 void push(int x) 31 { 32 int i=sz++; 33 while (i>0) 34 { 35 int p=(i-1)/2; 36 if (heap[p]<=x) break; 37 heap[i]=heap[p]; 38 i=p; 39 } 40 heap[i]=x; 41 } 42 43 int pop() 44 { 45 int ret=heap[0]; 46 int x=heap[--sz]; 47 int i=0; 48 while (i*2+1<sz) 49 { 50 int a=i*2+1, b=i*2+2; 51 if (b<sz && heap[b]<heap[a]) a=b; 52 if (heap[a]>=x) break; 53 heap[i]=heap[a]; 54 i=a; 55 } 56 heap[i]=x; 57 return ret; 58 }
- 再贴一段我自己的写法:(堆排序模板)
1 #include <iostream> 2 3 using namespace std; 4 5 int const MAX_N=100000; 6 int n; 7 int heap[MAX_N]; 8 9 void adjust(); 10 void down(int); 11 void up(int); 12 13 int main() 14 { 15 cin >> n; 16 for (int i=0; i<n; i++) cin >> heap[i]; 17 adjust(); 18 int sz=n; 19 for (int i=1; i<sz; i++) 20 { 21 int t=heap[0]; 22 heap[0]=heap[n-1]; 23 heap[--n]=t; 24 down(0); 25 } 26 for (int i=sz-1; i>=0; i--) cout << heap[i] << " "; 27 } 28 29 void adjust() 30 { 31 int f,t; 32 for (int i=n-1; i>0; i--) 33 { 34 if ((i-1)/2==(i-2)/2 && heap[i-1]<=heap[i]) f=(--i-1)/2; 35 else f=(i-1)/2; 36 if (heap[f]>heap[i]) 37 { 38 t=heap[f]; 39 heap[f]=heap[i]; 40 heap[i]=t; 41 down(i); 42 } 43 } 44 } 45 46 void down(int p) 47 { 48 int s,t; 49 while (p*2+1<n) 50 { 51 if (p*2+2<n && heap[p*2+2]<heap[p*2+1]) s=p*2+2; 52 else s=p*2+1; 53 if (heap[s]<heap[p]) 54 { 55 t=heap[s]; 56 heap[s]=heap[p]; 57 heap[p]=t; 58 p=s; 59 } 60 else break; 61 } 62 } 63 64 void up(int p) 65 { 66 int f,t; 67 while ((f=(p-1)/2)>=0) 68 { 69 if (heap[f]>heap[p]) 70 { 71 t=heap[f]; 72 heap[f]=heap[p]; 73 heap[p]=t; 74 p=f; 75 } 76 else break; 77 } 78 }
- 使用STL的priority_queue优先队列
注意:priority_queue取出数值时默认得到最大值1 #include <queue> 2 #include <cstdio> 3 using namespace std; 4 5 int main() 6 { 7 priority_queue<int> pque; 8 pque.push(3); 9 pque.push(5); 10 pque.push(1); 11 while (!pque.empty()) 12 { 13 printf("%d ",pque.top()); 14 pque.pop(); 15 } 16 return 0; 17 }
Expedition(POJ 2431)
- 原题如下:
Expedition
Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 23011 Accepted: 6512 Description
A group of cows grabbed a truck and ventured on an expedition deep into the jungle. Being rather poor drivers, the cows unfortunately managed to run over a rock and puncture the truck's fuel tank. The truck now leaks one unit of fuel every unit of distance it travels.
To repair the truck, the cows need to drive to the nearest town (no more than 1,000,000 units distant) down a long, winding road. On this road, between the town and the current location of the truck, there are N (1 <= N <= 10,000) fuel stops where the cows can stop to acquire additional fuel (1..100 units at each stop).
The jungle is a dangerous place for humans and is especially dangerous for cows. Therefore, the cows want to make the minimum possible number of stops for fuel on the way to the town. Fortunately, the capacity of the fuel tank on their truck is so large that there is effectively no limit to the amount of fuel it can hold. The truck is currently L units away from the town and has P units of fuel (1 <= P <= 1,000,000).
Determine the minimum number of stops needed to reach the town, or if the cows cannot reach the town at all.Input
* Line 1: A single integer, N
* Lines 2..N+1: Each line contains two space-separated integers describing a fuel stop: The first integer is the distance from the town to the stop; the second is the amount of fuel available at that stop.
* Line N+2: Two space-separated integers, L and POutput
* Line 1: A single integer giving the minimum number of fuel stops necessary to reach the town. If it is not possible to reach the town, output -1.Sample Input
4 4 4 5 2 11 5 15 10 25 10
Sample Output
2
Hint
INPUT DETAILS:
The truck is 25 units away from the town; the truck has 10 units of fuel. Along the road, there are 4 fuel stops at distances 4, 5, 11, and 15 from the town (so these are initially at distances 21, 20, 14, and 10 from the truck). These fuel stops can supply up to 4, 2, 5, and 10 units of fuel, respectively.
OUTPUT DETAILS:
Drive 10 units, stop to acquire 10 more units of fuel, drive 4 more units, stop to acquire 5 more units of fuel, then drive to the town. - 分析:在开车开往终点的途中,只有在加油站才可以加油,但是我们也可以这样来理解这个事情:“在到达加油站i时,就获得了一次在之后的任何时候都可以加Bi单位汽油的权利”,在解决这道问题时,这两者应该时一样的,而在之后需要油的时候,就认为是在之前经过的加油站加的油就可以了。由于我们希望在到达终点时,加油次数尽可能地少,所以可以在每次燃料为0的时候进行加油,很显然,每次燃料为0需要加油的时候就找之前经过的并且还没加过油的能加油量Bi最大的加油站,选择从大到小取值的优先队列来维护可用的Bi即可:在经过加油站i时,往优先队列里加入Bi,当燃料箱空了时,如果优先队列也为空,则无法到达终点,否则取出优先队列中的最大元素,并用来给卡车加油。
- 代码:
1 #include <queue> 2 #include <cstdio> 3 #include <algorithm> 4 using namespace std; 5 6 struct node 7 { 8 int a; 9 int b; 10 }; 11 12 const int MAX_N=10100; 13 int n; 14 int l,p; 15 node s[MAX_N+1]; 16 17 bool compare(const node &x, const node &y) 18 { 19 return x.a<y.a; 20 } 21 22 int main() 23 { 24 scanf("%d",&n); 25 for (int i=0; i<n; i++) 26 { 27 scanf("%d %d", &s[i].a, &s[i].b); 28 } 29 scanf("%d %d", &l, &p); 30 for (int i=0; i<n; i++) s[i].a=l-s[i].a; 31 s[n].a=l; 32 s[n].b=0; 33 sort(s,s+(++n),compare); 34 int ans=0,pos=0,tank=p; 35 priority_queue<int> pque; 36 for (int i=0; i<n; i++) 37 { 38 int d = s[i].a-pos; 39 while (tank-d<0) 40 { 41 if (pque.empty()) 42 { 43 puts("-1"); 44 return 0; 45 } 46 tank += pque.top(); 47 pque.pop(); 48 ans++; 49 } 50 tank -= d; 51 pos=s[i].a; 52 pque.push(s[i].b); 53 } 54 printf("%d ",ans); 55 }
Fence Repair(POJ 3253)
- 原题如下:
Fence Repair
Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 61237 Accepted: 20199 Description
Farmer John wants to repair a small length of the fence around the pasture. He measures the fence and finds that he needs N (1 ≤ N ≤ 20,000) planks of wood, each having some integer length Li (1 ≤ Li ≤ 50,000) units. He then purchases a single long board just long enough to saw into the N planks (i.e., whose length is the sum of the lengths Li). FJ is ignoring the "kerf", the extra length lost to sawdust when a sawcut is made; you should ignore it, too.
FJ sadly realizes that he doesn't own a saw with which to cut the wood, so he mosies over to Farmer Don's Farm with this long board and politely asks if he may borrow a saw.
Farmer Don, a closet capitalist, doesn't lend FJ a saw but instead offers to charge Farmer John for each of the N-1 cuts in the plank. The charge to cut a piece of wood is exactly equal to its length. Cutting a plank of length 21 costs 21 cents.
Farmer Don then lets Farmer John decide the order and locations to cut the plank. Help Farmer John determine the minimum amount of money he can spend to create the N planks. FJ knows that he can cut the board in various different orders which will result in different charges since the resulting intermediate planks are of different lengths.
Input
Line 1: One integer N, the number of planks
Lines 2..N+1: Each line contains a single integer describing the length of a needed plankOutput
Line 1: One integer: the minimum amount of money he must spend to make N-1 cutsSample Input
3 8 5 8
Sample Output
34
Hint
He wants to cut a board of length 21 into pieces of lengths 8, 5, and 8.
The original board measures 8+5+8=21. The first cut will cost 21, and should be used to cut the board into pieces measuring 13 and 8. The second cut will cost 13, and should be used to cut the 13 into 8 and 5. This would cost 21+13=34. If the 21 was cut into 16 and 5 instead, the second cut would cost 16 for a total of 37 (which is more than 34). - 分析:在贪心法一节中,给出了O(n2)的做法,如果使用优先队列来维护木板长度,则可以将复杂度降到O(nlogn)
- 代码:
1 #include <iostream> 2 #include <algorithm> 3 #include <functional> 4 #include <queue> 5 6 using namespace std; 7 8 const int MAX_N=20200; 9 int n; 10 int l[MAX_N-1]; 11 12 int main() 13 { 14 cin >> n; 15 for (int i=0; i<n; i++) cin >> l[i]; 16 long long ans=0; 17 priority_queue<int, vector<int>, greater<int>> que; 18 for (int i=0; i<n; i++) 19 { 20 que.push(l[i]); 21 } 22 while (que.size()>1) 23 { 24 int l1,l2; 25 l1=que.top(); 26 que.pop(); 27 l2=que.top(); 28 que.pop(); 29 ans+=l1+l2; 30 que.push(l1+l2); 31 } 32 cout << ans << endl; 33 }
二叉搜索树
- 一些注意点:
二叉搜索树的删除稍微麻烦一点,需要分情况来处理:
①需要删除的节点没有左儿子,那么就把右儿子提上去
②需要删除的节点的左儿子没有右儿子,那么就把左儿子提上去
③以上两种情况都不满足的话,就把左儿子的子孙中最大的节点提到需要删除的节点上 - 手动实现的二叉搜索树,书上代码:
1 #include <iostream> 2 3 using namespace std; 4 5 struct node 6 { 7 int val; 8 node *lch, *rch; 9 }; 10 11 node* insert(node *, int); 12 bool find(node *, int); 13 node *remove(node *, int); 14 void inorder(node *); 15 16 int main() 17 { 18 node *root=NULL; 19 int n; 20 cin >> n; 21 int x; 22 for (int i=0; i<n; i++) 23 { 24 cin >> x; 25 root=insert(root,x); 26 } 27 inorder(root); 28 if (find(root,8)) root=remove(root,8); 29 if (find(root,4)) root=remove(root,4); 30 if (find(root,7)) root=remove(root,7); 31 inorder(root); 32 } 33 34 node *insert(node *p, int x) 35 { 36 if (p==NULL) 37 { 38 node *q = new node; 39 q->val=x; 40 q->lch=q->rch=NULL; 41 return q; 42 } 43 else 44 { 45 if (x<p->val) p->lch=insert(p->lch,x ); 46 else p->rch=insert(p->rch, x); 47 return p; 48 } 49 } 50 51 bool find(node *p, int x) 52 { 53 if (p==NULL) return false; 54 else if (x==p->val) return true; 55 else if (x<p->val) return find(p->lch, x); 56 else return find(p->rch, x); 57 } 58 59 node* remove(node *p, int x) 60 { 61 if (p==NULL) return NULL; 62 else if (x<p->val) p->lch=remove(p->lch, x); 63 else if (x>p->val) p->rch=remove(p->rch, x); 64 else if (p->lch==NULL) 65 { 66 node *q=p->rch; 67 delete p; 68 return q; 69 } 70 else if (p->lch->rch==NULL) 71 { 72 node *q = p->lch; 73 q->rch=p->rch; 74 delete p; 75 return q; 76 } 77 else 78 { 79 node *q; 80 for (q=p->lch; q->rch->rch!=NULL; q=q->rch); 81 node *r=q->rch; 82 q->rch=r->lch; 83 r->lch=p->lch; 84 r->rch=p->rch; 85 delete p; 86 return r; 87 } 88 return p; 89 } 90 91 void inorder(node *p) 92 { 93 if (p==NULL) return; 94 inorder(p->lch); 95 cout << p->val << " "; 96 inorder(p->rch); 97 }
- 使用STL里实现的二叉搜索树
STL里有set和map容器,set是使用二叉搜索树维护集合的容器,map则是维护键和键对应的值的容器,此外还有能存放重复键值的multiset和multimap等容器。
set的使用:1 #include <cstdio> 2 #include <set> 3 using namespace std; 4 int main() 5 { 6 //声明 7 set<int> s; 8 9 //插入元素 10 s.insert(1); 11 s.insert(3); 12 s.insert(5); 13 14 //查找元素 15 set<int> ::iterator ite; 16 ite=s.find(1); 17 if (ite==s.end()) puts("not found"); 18 else puts("found"); 19 ite=s.find(2); 20 if (ite==s.end()) puts("not found"); 21 else puts("found"); 22 23 //删除元素 24 s.erase(3); 25 26 //其它的查找元素的方法 27 if (s.count(3)!=0) puts("found"); 28 else puts("not found"); 29 30 //遍历所有元素 31 for (ite=s.begin(); ite!=s.end(); ++ite) 32 { 33 printf("%d ",*ite); 34 } 35 36 //清空集合 37 s.clear(); 38 39 return 0; 40 }
map的使用:
1 #include <cstdio> 2 #include <map> 3 #include <string> 4 using namespace std; 5 int main() 6 { 7 //声明 8 map<int, const char*> m; 9 10 //插入元素 11 m.insert(make_pair(1, "ONE")); 12 m.insert(make_pair(10, "TEN")); 13 m[100]="HUNDRED";//其它的写法 14 15 //查找元素 16 map<int, const char*> ::iterator ite; 17 ite=m.find(1); 18 puts(ite->second);//(输出)ONE 19 20 ite=m.find(2); 21 if (ite==m.end()) puts("not found"); 22 else puts(ite->second); 23 24 puts(m[10]);//其它的写法 25 26 //删除元素 27 m.erase(10); 28 29 //遍历一边所有元素 30 for (ite=m.begin(); ite!=m.end(); ++ite) 31 { 32 printf("%d: %s ", ite->first, ite->second); 33 } 34 35 //清空map 36 m.clear(); 37 38 return 0; 39 }
并查集
- 一些注意点:
并查集的两个优化:①合并时rank值小的向大的连边②路径压缩:查询过程中经过的所有节点改为直接连到根上
加入两个优化后并查集的效率非常高,对n个元素的并查集进行一次操作的均摊复杂度为O(α(n)),α(n)是阿克曼(Ackermann)函数的反函数,比O(logn)还要快 - 并查集的实现
1 #include <iostream> 2 using namespace std; 3 4 const int MAX_N=10000; 5 int par[MAX_N]; 6 int r[MAX_N]; 7 int n; 8 9 void init(int n);//初始化n个元素 10 int find(int x);//查询树的根 11 void unite(int x, int y);//合并x和y所属的集合 12 bool same(int x, int y);//判断x和y是否属于同一个集合 13 14 int main() 15 { 16 cin >> n; 17 init(n); 18 unite(n/2,n/2+1); 19 cout << same(1,2) << endl; 20 cout << same(5,6) << endl; 21 } 22 23 void init(int n) 24 { 25 for (int i=0; i<n; i++) 26 { 27 r[i]=0; 28 par[i]=i; 29 } 30 } 31 32 int find(int x) 33 { 34 if (par[x]==x) return x; 35 else return par[x]=find(par[x]); 36 } 37 38 void unite(int x, int y) 39 { 40 x=find(x); 41 y=find(y); 42 if (x==y) return; 43 if (r[x]<r[y]) par[x]=y; 44 else 45 { 46 par[y]=x; 47 if (r[x]==r[y]) ++r[x]; 48 } 49 } 50 51 bool same(int x, int y) 52 { 53 return find(x)==find(y); 54 }
食物链(POJ 1182)
- 原题如下:
食物链
Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 90889 Accepted: 27314 Description
动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。
现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这N个动物所构成的食物链关系进行描述:
第一种说法是"1 X Y",表示X和Y是同类。
第二种说法是"2 X Y",表示X吃Y。
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
1) 当前的话与前面的某些真的话冲突,就是假话;
2) 当前的话中X或Y比N大,就是假话;
3) 当前的话表示X吃X,就是假话。
你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。Input
第一行是两个整数N和K,以一个空格分隔。
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。Output
只有一个整数,表示假话的数目。Sample Input
100 7 1 101 1 2 1 2 2 2 3 2 3 3 1 1 3 2 3 1 1 5 5
Sample Output
3
- 分析:
本题中,并不只有属于同一类的信息,还有捕食关系的存在,因此要想办法维护这些关系。
对于每只动物i,创建3个元素i-A,i-B,i-C,并用这3*N个元素建立并查集,用来维护这些信息:①i-x表示i属于种类x,②并查集里的每一个组表示组内所有元素代表的情况都同时发生或不发生
对于每一条信息,按照要求进行合并即可,但是要在合并前判断一下是否会产生矛盾。 - 代码:
1 #include <cstdio> 2 3 using namespace std; 4 5 int n,k; 6 int *T,*X,*Y,*par,*r; 7 8 void init(int n) 9 { 10 for (int i=0; i<n; i++) 11 { 12 par[i]=i; 13 r[i]=0; 14 } 15 } 16 17 int find(int x) 18 { 19 if (par[x]==x) return x; 20 return par[x]=find(par[x]); 21 } 22 23 void unite(int x, int y) 24 { 25 x=find(x); 26 y=find(y); 27 if (x==y) return; 28 if (r[x]<r[y]) par[x]=y; 29 else 30 { 31 par[y]=x; 32 if(r[x]==r[y]) ++r[x]; 33 } 34 } 35 36 bool same(int x, int y) 37 { 38 return find(x)==find(y); 39 } 40 41 int main() 42 { 43 scanf("%d %d",&n,&k); 44 T=new int[k]; 45 X=new int[k]; 46 Y=new int[k]; 47 par=new int[n*3]; 48 r=new int[n*3]; 49 for (int i=0; i<k; i++) scanf("%d %d %d",&T[i],&X[i],&Y[i]); 50 //元素x,x+n,x+n*2分别表示x-A,X-B,x-C 51 init(n*3); 52 int ans=0; 53 for (int i=0; i<k; i++) 54 { 55 int t=T[i],x=X[i]-1,y=Y[i]-1; 56 if (x<0 || x>=n || y<0 || y>=n) 57 { 58 ++ans; 59 continue; 60 } 61 if (t==1) 62 { 63 if (same(x,y+n) || same(x,y+n*2)) ans++; 64 else for (int j=0; j<3; j++) unite(x+n*j,y+n*j); 65 } 66 else 67 { 68 if (same(x,y) || same(x,y+n*2)) ans++; 69 else for (int j=0; j<3; j++) unite(x+n*j,y+n*((j+1)%3)); 70 } 71 } 72 printf("%d ",ans); 73 }
PS:顺便吐槽一下cin、cout的效率,这题一开始用cin、cout一直TLE,后来换了scanf和printf才AC。。。