3.1 基础数据结构回顾
例题1 uva11995 http://acm.hust.edu.cn/vjudge/problem/18700
猜测符合哪种数据结构 , 用stl模拟判断.
1 //#define txtout 2 //#define debug 3 #include<bits/stdc++.h> 4 #define mt(a,b) memset(a,b,sizeof(a)) 5 using namespace std; 6 typedef long long LL; 7 const double pi=acos(-1.0); 8 const double eps=1e-8; 9 const int inf=0x3f3f3f3f; 10 const int M=1e5+10; 11 int n; 12 int a[M]; 13 int b[M]; 14 char answer[8][32]={"stack","queue","priority queue","impossible","not sure"}; 15 stack<int> s; 16 queue<int> q; 17 priority_queue<int> p; 18 bool checkStack(){ 19 while(!s.empty()) s.pop(); 20 for(int i=0;i<n;i++){ 21 if(a[i]==1){ 22 s.push(b[i]); 23 continue; 24 } 25 if(s.empty()) return false; 26 if(s.top()!=b[i]) return false; 27 s.pop(); 28 } 29 return true; 30 } 31 bool checkQueue(){ 32 while(!q.empty()) q.pop(); 33 for(int i=0;i<n;i++){ 34 if(a[i]==1){ 35 q.push(b[i]); 36 continue; 37 } 38 if(q.empty()) return false; 39 if(q.front()!=b[i]) return false; 40 q.pop(); 41 } 42 return true; 43 } 44 bool checkPriority(){ 45 while(!p.empty()) p.pop(); 46 for(int i=0;i<n;i++){ 47 if(a[i]==1){ 48 p.push(b[i]); 49 continue; 50 } 51 if(p.empty()) return false; 52 if(p.top()!=b[i]) return false; 53 p.pop(); 54 } 55 return true; 56 } 57 int solve(){ 58 bool isStack=checkStack(); 59 bool isQueue=checkQueue(); 60 bool isPriority=checkPriority(); 61 if(isStack&&!isQueue&&!isPriority) return 0; 62 if(!isStack&&isQueue&&!isPriority) return 1; 63 if(!isStack&&!isQueue&&isPriority) return 2; 64 if(!isStack&&!isQueue&&!isPriority) return 3; 65 return 4; 66 } 67 int main(){ 68 #ifdef txtout 69 freopen("in.txt","r",stdin); 70 freopen("out.txt","w",stdout); 71 #endif // txtout 72 while(~scanf("%d",&n)){ 73 for(int i=0;i<n;i++){ 74 scanf("%d%d",&a[i],&b[i]); 75 } 76 puts(answer[solve()]); 77 } 78 return 0; 79 }
例题2 uva 11991 http://acm.hust.edu.cn/vjudge/problem/18696
n个正整数, m 次查询 都是10^5, 每次查询第k 个 值为v的下标. 开10的6次方vector,每个值的下表到对应vector, o1查询. on预处理
1 //#define txtout 2 //#define debug 3 #include<bits/stdc++.h> 4 #define mt(a,b) memset(a,b,sizeof(a)) 5 using namespace std; 6 typedef long long LL; 7 const double pi=acos(-1.0); 8 const double eps=1e-8; 9 const int inf=0x3f3f3f3f; 10 const int M=1e5+10; 11 int n,m; 12 int a[M]; 13 int k[M]; 14 int v[M]; 15 int answer[M]; 16 vector<int> id[M*10]; 17 void solve(){ 18 for(int i=0;i<n;i++){ 19 id[a[i]].clear(); 20 } 21 for(int i=0;i<n;i++){ 22 id[a[i]].push_back(i+1); 23 } 24 for(int i=0;i<m;i++){ 25 int value=v[i]; 26 if(id[value].size()<k[i]){ 27 answer[i]=0; 28 } 29 else{ 30 answer[i]=id[value][k[i]-1]; 31 } 32 } 33 } 34 int main(){ 35 #ifdef txtout 36 freopen("in.txt","r",stdin); 37 freopen("out.txt","w",stdout); 38 #endif // txtout 39 while(~scanf("%d%d",&n,&m)){ 40 for(int i=0;i<n;i++){ 41 scanf("%d",&a[i]); 42 } 43 for(int i=0;i<m;i++){ 44 scanf("%d%d",&k[i],&v[i]); 45 } 46 solve(); 47 for(int i=0;i<m;i++){ 48 printf("%d\n",answer[i]); 49 } 50 } 51 return 0; 52 }
白书用 map <int, vector<int> > ,也是一种写法 nlogn
1 //#define txtout 2 //#define debug 3 #include<bits/stdc++.h> 4 #define mt(a,b) memset(a,b,sizeof(a)) 5 using namespace std; 6 typedef long long LL; 7 const double pi=acos(-1.0); 8 const double eps=1e-8; 9 const int inf=0x3f3f3f3f; 10 const int M=1e5+10; 11 int n,m; 12 map<int, vector<int> > a; 13 int main(){ 14 #ifdef txtout 15 freopen("in.txt","r",stdin); 16 freopen("out.txt","w",stdout); 17 #endif // txtout 18 while(~scanf("%d%d",&n,&m)){ 19 a.clear(); 20 int x,y; 21 for(int i=0;i<n;i++){ 22 scanf("%d",&x); 23 if(!a.count(x)) a[x]=vector<int>(); 24 a[x].push_back(i+1); 25 } 26 for(int i=0;i<m;i++){ 27 scanf("%d%d",&x,&y); 28 if(!a.count(y)||a[y].size()<x){ 29 puts("0"); 30 continue; 31 } 32 printf("%d\n",a[y][x-1]); 33 } 34 } 35 return 0; 36 }
例题3 la3135 http://acm.hust.edu.cn/vjudge/problem/18684
输入 A B, 表示每B秒产生A编号, 输出前k个编号, 同时产生的优先选编号小的。
优先队列,规则时间小的靠前,时间相同编号小的考前。优先队列大的在堆顶。
1 //#define txtout 2 //#define debug 3 #include<bits/stdc++.h> 4 #define mt(a,b) memset(a,b,sizeof(a)) 5 using namespace std; 6 typedef long long LL; 7 const double pi=acos(-1.0); 8 const double eps=1e-8; 9 const int inf=0x3f3f3f3f; 10 const int M=1e5+10; 11 int n; 12 char a[M]; 13 struct Q{ 14 int id,now,add; 15 bool operator <(const Q &b) const { 16 return now>b.now||(now==b.now&&id>b.id); 17 } 18 }now,pre; 19 priority_queue<Q> q; 20 int main(){ 21 #ifdef txtout 22 freopen("in.txt","r",stdin); 23 freopen("out.txt","w",stdout); 24 #endif // txtout 25 while(!q.empty()) q.pop(); 26 while(~scanf("%s",a)){ 27 if(!strcmp(a,"#")){ 28 scanf("%d",&n); 29 while(n--){ 30 pre=q.top(); 31 q.pop(); 32 printf("%d\n",pre.id); 33 now.id=pre.id; 34 now.add=pre.add; 35 now.now=pre.now+pre.add; 36 q.push(now); 37 } 38 while(!q.empty()) q.pop(); 39 continue; 40 } 41 int id,t; 42 scanf("%d%d",&id,&t); 43 now.id=id; 44 now.now=now.add=t; 45 q.push(now); 46 } 47 return 0; 48 }
例题4 uva11997 http://acm.hust.edu.cn/vjudge/problem/18702
给k*k矩阵 每行选一个, 求和, 那么一共有 k的k次方种和, 输出前k大的和
做法, 两行两行的做, 当k只有两行的时候 k^2暴力 ,然后取前k个是可以接受的. 当有3行或以上时 , 可以看出 之前两行的前k个小的才有用 , 所以用这前k个 与下一行 k^2枚举 然后得到的和再取前k个.
实现的时候,用优先队列维护,保持队列里最多有k个就可以,因为大于k的最终也会被淘汰,所以没必要存,并且可以通过当前最大的值剪枝. 不算剪枝是k^3logk的实际上挺快 300ms
1 //#define txtout 2 //#define debug 3 #include<bits/stdc++.h> 4 #define mt(a,b) memset(a,b,sizeof(a)) 5 using namespace std; 6 typedef long long LL; 7 const double pi=acos(-1.0); 8 const double eps=1e-8; 9 const int inf=0x3f3f3f3f; 10 const int M=1e3+10; 11 int n; 12 int a[M][M]; 13 vector<int> answer; 14 priority_queue<int> q; 15 void solve(){ 16 for(int i=0;i<n;i++){ 17 sort(a[i],a[i]+n); 18 } 19 answer.clear(); 20 while(!q.empty()) q.pop(); 21 for(int i=0;i<n;i++){ 22 for(int j=0;j<n;j++){ 23 q.push(a[0][i]+a[1][j]); 24 if(q.size()>n) q.pop(); 25 } 26 } 27 while(!q.empty()){ 28 answer.push_back(q.top()); 29 q.pop(); 30 } 31 for(int i=2;i<n;i++){ 32 while(!q.empty()) q.pop(); 33 for(int j=0;j<n;j++){ 34 for(int k=0;k<n;k++){ 35 int sum=answer[j]+a[i][k]; 36 if(q.size()==n&&q.top()<=sum) break; 37 q.push(sum); 38 if(q.size()>n) q.pop(); 39 } 40 } 41 answer.clear(); 42 while(!q.empty()){ 43 answer.push_back(q.top()); 44 q.pop(); 45 } 46 } 47 sort(answer.begin(),answer.end()); 48 } 49 int main(){ 50 #ifdef txtout 51 freopen("in.txt","r",stdin); 52 freopen("out.txt","w",stdout); 53 #endif // txtout 54 while(~scanf("%d",&n)){ 55 for(int i=0;i<n;i++){ 56 for(int j=0;j<n;j++){ 57 scanf("%d",&a[i][j]); 58 } 59 } 60 solve(); 61 for(int i=0;i<answer.size();i++){ 62 if(i) putchar(' '); 63 printf("%d",answer[i]); 64 } 65 puts(""); 66 } 67 return 0; 68 }
白书写法100ms, 在合并的时候不是k^2的,合并AB两个数组时,先把AB排序, 会有 k^2的情况, 但是 A1 和B1-Bn 的和是单调增的 , 所以相当于k个有序的一维数组,
A1 B1 --- A1 Bn
AnB1 --- An Bn
每一行都是递增的, 现在要从这k*k个数中取前k大, 那么先考虑把第一列都放进优先队列, 然后优先队列每次弹出最小的, 弹出后, 再压入下一个 比如弹出 AiB1 那么下一个就是AiB2. 这样合并就是klogk的
1 //#define txtout 2 //#define debug 3 #include<bits/stdc++.h> 4 #define mt(a,b) memset(a,b,sizeof(a)) 5 using namespace std; 6 typedef long long LL; 7 const double pi=acos(-1.0); 8 const double eps=1e-8; 9 const int inf=0x3f3f3f3f; 10 const int M=1e3+10; 11 12 struct G{ 13 int s,b; 14 G(int s,int b):s(s),b(b){} 15 bool operator <(const G &b) const { 16 return s>b.s; 17 } 18 }; 19 20 void Merge(int* A,int* B,int* C,int n){ 21 priority_queue<G> q; 22 for(int i=0;i<n;i++){ 23 q.push(G(A[i]+B[0],0)); 24 } 25 for(int i=0;i<n;i++){ 26 G g=q.top(); q.pop(); 27 C[i]=g.s; 28 int b=g.b; 29 if(b+1<n) q.push(G(g.s-B[b]+B[b+1],b+1)); 30 } 31 } 32 33 int A[M][M]; 34 35 int main(){ 36 #ifdef txtout 37 freopen("in.txt","r",stdin); 38 freopen("out.txt","w",stdout); 39 #endif // txtout 40 int n; 41 while(~scanf("%d",&n)){ 42 for(int i=0;i<n;i++){ 43 for(int j=0;j<n;j++){ 44 scanf("%d",&A[i][j]); 45 } 46 sort(A[i],A[i]+n); 47 } 48 for(int i=1;i<n;i++){ 49 Merge(A[0],A[i],A[0],n); 50 } 51 for(int i=0;i<n;i++){ 52 if(i) putchar(' '); 53 printf("%d",A[0][i]); 54 } 55 puts(""); 56 } 57 return 0; 58 }
整体复杂度k^2logk 比我好k ,但实际 好了3倍,说明剪枝能减去很多.
例题5 la3644 http://acm.hust.edu.cn/vjudge/problem/12648
按顺序加边,无向图,如果出现环则这条边不能加,问最后有几条边不能加.
用并查集,如果本来是一个集合 ,说明不能加.
1 //#define txtout 2 //#define debug 3 #include<bits/stdc++.h> 4 #define mt(a,b) memset(a,b,sizeof(a)) 5 using namespace std; 6 typedef long long LL; 7 const double pi=acos(-1.0); 8 const double eps=1e-8; 9 const int inf=0x3f3f3f3f; 10 const int M=1e5+10; 11 class UnionFindSet { ///并查集 12 static const int MV=1e5+10; ///点的个数 13 int par[MV],num[MV]; 14 void add(int son,int fa) { 15 par[fa]+=par[son]; 16 par[son]=fa; 17 num[fa]+=num[son]; 18 } 19 public: 20 void init(int n) { 21 for(int i=0; i<=n; i++) { 22 num[i]=1; 23 par[i]=-1; 24 } 25 } 26 int getroot(int x) { 27 int i=x,j=x,temp; 28 while(par[i]>=0) i=par[i]; 29 while(j!=i) { 30 temp=par[j]; 31 par[j]=i; 32 j=temp; 33 } 34 return i; 35 } 36 bool unite(int x,int y) { 37 int p=getroot(x); 38 int q=getroot(y); 39 if(p==q) return false; 40 if(par[p]>par[q]) { 41 add(p,q); 42 } 43 else { 44 add(q,p); 45 } 46 return true; 47 } 48 int getnum(int id) { ///返回该点所在集合包含的点数 49 return num[getroot(id)]; 50 } 51 } gx; 52 int main() { 53 #ifdef txtout 54 freopen("in.txt","r",stdin); 55 freopen("out.txt","w",stdout); 56 #endif // txtout 57 gx.init(1e5+5); 58 int answer=0; 59 int a,b; 60 while(~scanf("%d",&a)) { 61 if(a==-1){ 62 printf("%d\n",answer); 63 gx.init(1e5+5); 64 answer=0; 65 continue; 66 } 67 scanf("%d",&b); 68 if(!gx.unite(a,b)) answer++; 69 } 70 return 0; 71 }
简洁一点的白书的实现方式.
1 //#define txtout 2 //#define debug 3 #include<bits/stdc++.h> 4 #define mt(a,b) memset(a,b,sizeof(a)) 5 using namespace std; 6 typedef long long LL; 7 const double pi=acos(-1.0); 8 const double eps=1e-8; 9 const int inf=0x3f3f3f3f; 10 const int M=1e5+10; 11 class UnionFindSet { ///并查集 12 static const int MV=1e5+10; ///点的个数 13 int par[MV]; 14 public: 15 void init(int n) { 16 for(int i=0; i<=n; i++) { 17 par[i]=i; 18 } 19 } 20 int getroot(int x) { 21 return x==par[x]?x:par[x]=getroot(par[x]); 22 } 23 bool unite(int x,int y) { 24 int p=getroot(x); 25 int q=getroot(y); 26 if(p==q) return false; 27 par[p]=q; 28 return true; 29 } 30 } gx; 31 int main() { 32 #ifdef txtout 33 freopen("in.txt","r",stdin); 34 freopen("out.txt","w",stdout); 35 #endif // txtout 36 gx.init(1e5+5); 37 int answer=0; 38 int a,b; 39 while(~scanf("%d",&a)) { 40 if(a==-1){ 41 printf("%d\n",answer); 42 gx.init(1e5+5); 43 answer=0; 44 continue; 45 } 46 scanf("%d",&b); 47 if(!gx.unite(a,b)) answer++; 48 } 49 return 0; 50 }
例题6 la3027 http://acm.hust.edu.cn/vjudge/problem/33982
操作 把A的父亲设置为B, 距离 w, 查询 A距离根的距离.
白书带权并查集模板
1 //#define txtout 2 //#define debug 3 #include<bits/stdc++.h> 4 #define mt(a,b) memset(a,b,sizeof(a)) 5 using namespace std; 6 typedef long long LL; 7 const double pi=acos(-1.0); 8 const double eps=1e-8; 9 const int inf=0x3f3f3f3f; 10 const int M=1e5+10; 11 class WeightedUnionFindSet { ///带权并查集 12 static const int MV=2e4+10; ///点的个数 13 int par[MV]; 14 int dist[MV]; 15 public: 16 void init(int n) { 17 for(int i=0; i<=n; i++) { 18 par[i]=i; 19 dist[i]=0; 20 } 21 } 22 int getdist(int x){ 23 getroot(x); 24 return dist[x]; 25 } 26 int getroot(int x) { 27 if(x==par[x]) return x; 28 int root=getroot(par[x]); 29 dist[x]+=dist[par[x]]; 30 return par[x]=root; 31 } 32 void unite(int x,int y,int w) { 33 par[x]=y; 34 dist[x]=w; 35 } 36 } gx; 37 int main() { 38 #ifdef txtout 39 freopen("in.txt","r",stdin); 40 freopen("out.txt","w",stdout); 41 #endif // txtout 42 int t,n,x,y; 43 char op[4]; 44 while(~scanf("%d",&t)) { 45 while(t--){ 46 scanf("%d",&n); 47 gx.init(n); 48 while(true){ 49 scanf("%s",op); 50 if(op[0]=='O') break; 51 if(op[0]=='E'){ 52 scanf("%d",&x); 53 printf("%d\n",gx.getdist(x)); 54 continue; 55 } 56 scanf("%d%d",&x,&y); 57 gx.unite(x,y,abs(x-y)%1000); 58 } 59 } 60 } 61 return 0; 62 }
end