清北学堂Day3
Posted 小蒟蒻
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了清北学堂Day3相关的知识,希望对你有一定的参考价值。
Part1--模拟题
今天的模拟题简直全面爆炸。一共才拿了30分。
第一题原来做过一道差不多的。然后我就仗着自己听过一遍正解瞎写。嗯,就是根本没有认真想一下这么写下去会不会有什么问题。结果……
第二题拿了20。老师本来说是要用线段树才能正解,然后我想哦那我也正解不了,心中有了一丝丝的宽慰。后来老师又想到了一种不用线段树的做法。而且我发现这个做法和我的想法很像啊!就有一点点我没考虑到。于是特别的气愤。
第一题:
括号序列(bracket)
Time Limit:1000ms Memory Limit:128MB
题目描述
LYK有一个括号序列,但这个序列不一定合法。
一个合法的括号序列如下:
()是合法的括号序列。
若A是合法的括号序列,则(A)是合法的括号序列。
若A和B分别是合法的括号序列,则AB是合法的括号序列。
LYK想通过尽可能少的操作将这个不一定合法的括号序列变成合法的括号序列。一次修改操作是将某个字符变成另一个字符。
你能帮帮它吗?
输入格式(bracket.in)
一行一个字符串S。
输出格式(bracket.out)
一个数表示最少修改次数。
输入样例
()))
输出样例
1
样例解释
将第二个字符修改成(即可。
数据范围
对于30%的数据|S|<=10。
对于60%的数据|S|<=1000。
对于100%的数据|S|<=100000。且|S|是偶数。
先附上我的错误代码:
1 #include <iostream> 2 #include <cmath> 3 #include <cstring> 4 #include <cstdio> 5 #include <cstdlib> 6 #include <algorithm> 7 using namespace std; 8 char s[101010]; 9 int a[101010]; 10 int main() 11 { 12 13 freopen("bracket.in","r",stdin); 14 freopen("bracket.out","w",stdout); 15 16 scanf("%s",s); 17 for(int i=0;i<strlen(s);i++) 18 { 19 if(s[i]==\'(\') a[i+1]=1; 20 if(s[i]==\')\') a[i+1]=-1; 21 } 22 for(int i=1;i<=strlen(s);i++) 23 { 24 if(a[i]<0) continue; 25 for(int j=strlen(s);j>i;j--) 26 { 27 if(a[j]>0) continue; 28 a[i]=0;a[j]=0; 29 break; 30 } 31 } 32 int ans=0; 33 for(int i=1;i<=strlen(s);i++) 34 { 35 if(a[i]!=0) ans++; 36 } 37 printf("%d",ans/2); 38 return 0; 39 }
一组错误答案:
正确答案应该是2。
也就是说,我的代码没有考虑到)( 、)))((([即有奇数组完全相反的括号]的情况
我的第二个循环结束后,对于这种情况没有进行任何操作。
然而手算就可以知道,)))(((这种情况需要改变4次而不是3次,因此不能直接/2
下面放上标程:
1 #include <iostream> 2 #include <cmath> 3 #include <cstdio> 4 #include <cstdlib> 5 #include <iostream> 6 #include <string> 7 #include <cstring> 8 using namespace std; 9 char s[100005]; 10 int i,p,o,ans; 11 int main() 12 { 13 freopen("bracket.in","r",stdin); 14 freopen("bracket.out","w",stdout); 15 scanf("%s",s); p=strlen(s); 16 for (i=0; i<p; i++) 17 { 18 if (s[i]==\')\') 19 { 20 if (o==0){o++; ans++;} else 21 o--; 22 } 23 else 24 o++; 25 } 26 cout<<ans+o/2; 27 // system("pause"); 28 return 0; 29 }
简单来说,标程的思路是:
从左往右扫过来,遇到一个‘)’,观察前面有没有‘(’。若有则抵消掉,否则把这个字符变成‘(’一直这么做,扫完整个字符串之后一定剩下一堆左括号。把剩下的括号总数/2
总结:这道题就是很简单的一道题,只不过受了之前思路的影响,导致想偏了。这告诉我以后看到熟悉的题不能上来就写,更应该仔细思考其中的差异。
第二题:
公交车(bus)
Time Limit:1000ms Memory Limit:128MB
题目描述
LYK在玩一个游戏。
有k群小怪兽想乘坐公交车。第i群小怪兽想从xi出发乘坐公交车到yi。但公交车的容量只有M,而且这辆公交车只会从1号点行驶到n号点。
LYK想让小怪兽们尽可能的到达自己想去的地方。它想知道最多能满足多少小怪兽的要求。
当然一群小怪兽没必要一起上下车,它们是可以被分开来的。
输入格式(bus.in)
第一行三个数k,n,M。
接下来k行每行3个数xi,yi和ci。其中ci表示第i群小怪兽的小怪兽数量。
输出格式(bus.out)
一个数表示最多有多少只小怪兽能满足要求。
输入样例
3 5 3
1 3 4
3 5 2
1 5 3
输出样例
5
样例解释
第一群的3只小怪兽在1号点上车,并在3号点下车。
第二群的2只小怪兽在3号点上车,5号点下车。
数据范围
对于30%的数据小怪兽的总数不超过10只,n<=10。
对于另外30%的数据k,n<=1000。
对于100%的数据1<=n<=20000,1<=k<=50000,1<=M<=100,1<=ci<=100,1<=xi<yi<=n。
我的想法:首先,把输入的小怪兽按照上车顺序、下车顺序和在车上呆的时间排序(具体可以看代码)。之后从1~n扫一遍,能上车的上,容量满了就跳过,知道有小怪兽下车,腾出容量
这个想法的bug在于:如果有一群小怪兽,1上车,n下车,而且他们上车后就满载了,那么中间如果有几群小怪兽,他们上下车间隔的时间段特别短,而且总数量多于车的满载数,那么答案就错了。所以这道题的贪心并不完全在于车上有多少人,或者有多少时间车上没人,而是更多的在于怎样让这辆车在1~n的时间里承载尽量多的小怪兽。
我的代码:
1 #include <iostream> 2 #include <cmath> 3 #include <cstring> 4 #include <cstdio> 5 #include <cstdlib> 6 #include <algorithm> 7 #include <queue> 8 using namespace std; 9 10 struct data 11 { 12 int x,y,c; 13 bool operator <(const data&o)const 14 {/**/ 15 if(x<o.x) return true; 16 else if(x==o.x && y<o.y) return true; 17 else if(x==o.x && y==o.y && c>o.c) return true; 18 else return false; 19 } 20 }a[20101]; 21 22 int main() 23 { 24 25 freopen("bus.in","r",stdin); 26 freopen("bus.out","w",stdout); 27 28 int k,n,m; 29 scanf("%d%d%d",&k,&n,&m); 30 for(int i=1;i<=k;i++) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].c); 31 sort(a+1,a+k+1); 32 int w=1,ans=0; 33 queue <int> q; 34 for(int i=1;i<=n;i++) 35 { 36 if(!q.empty()) 37 { 38 while(q.front()==i) {q.pop();ans++;} 39 } 40 while(a[w].x<i) w++; 41 while(a[w].x==i && q.size()<m) 42 { 43 for(int j=1;j<=a[w].c;j++) 44 if(q.size()<m) q.push(a[w].y); 45 else break; 46 w++; 47 } 48 49 } 50 printf("%d",ans); 51 return 0; 52 }
非线段树AC思路:
首先我们知道一个经典问题:有n个区间,找尽可能多的区间使得区间之间不相交。
这道题的做法就是按右端点从小到大排序,然后找其中的不相交区间就好了。
第二题可以说是这道题的一个变形。易证如果不取任意可取的线段,则会浪费一段空间。(说白了就是一个贪心。拿到60分)
100分:维护贪心。维护一个f[i]表示i这个时刻,车上已经坐了几只怪兽。
因为是老师临时想出来的,没有标程。
下面是线段树做法。里面应该是有线段树的模板的,可以借这道题背下来。
1 //线段树模板! 2 #include <cmath> 3 #include <cstdio> 4 #include <cstdlib> 5 #include <iostream> 6 #include <algorithm> 7 using namespace std; 8 int tree[65536][4],n,m,c,i,MIN,ans; 9 struct node 10 { 11 int x; 12 int y; 13 int z; 14 }; 15 node A[100005]; 16 int cmp(node i,node j) 17 { 18 return i.y<j.y; 19 } 20 void Update(int k) 21 { 22 tree[k*2][2]+=tree[k][3]; 23 tree[k*2+1][2]+=tree[k][3]; 24 tree[k*2][3]+=tree[k][3]; 25 tree[k*2+1][3]+=tree[k][3]; 26 tree[k][3]=0; 27 } 28 void work(int root,int l,int r,int k) 29 { 30 if (l==tree[root][0] && r==tree[root][1]) 31 { 32 tree[root][2]+=k; 33 tree[root][3]+=k; 34 return; 35 } 36 Update(root); 37 int mid=(tree[root][0]+tree[root][1])/2; 38 if (l<=mid) work(root*2,l,min(mid,r),k); 39 if (r>mid) work(root*2+1,max(mid+1,l),r,k); 40 tree[root][2]=min(tree[root*2][2],tree[root*2+1][2]); 41 } 42 int find(int root,int l,int r) 43 { 44 if (l==tree[root][0] && r==tree[root][1]) return tree[root][2]; 45 Update(root); 46 int mid=(tree[root][0]+tree[root][1])/2,p=453266144,q=453266144; 47 if (l<=mid) p=find(root*2,l,min(mid,r)); 48 if (r>mid) q=find(root*2+1,max(mid+1,l),r); 49 return min(p,q); 50 } 51 int main() 52 { 53 freopen("bus.in","r",stdin); 54 freopen("bus.out","w",stdout); 55 scanf("%d%d%d",&n,&m,&c); 56 for (i=32768; i<=65535; i++) tree[i][0]=tree[i][1]=i; 57 for (i=32767; i>=1; i--) 58 { 59 tree[i][0]=tree[i*2][0]; 60 tree[i][1]=tree[i*2+1][1]; 61 } 62 work(1,1+32767,m+32767,c); 63 for (i=1; i<=n; i++) 64 { 65 scanf("%d%d%d",&A[i].x,&A[i].y,&A[i].z); 66 A[i].y--; 67 } 68 sort(A+1,A+n+1,cmp); 69 for (i=1; i<=n; i++) 70 { 71 MIN=find(1,A[i].x+32767,A[i].y+32767); 72 if (MIN>A[i].z) 73 { 74 work(1,A[i].x+32767,A[i].y+32767,-A[i].z); 75 ans+=A[i].z; 76 } 77 else 78 { 79 work(1,A[i].x+32767,A[i].y+32767,-MIN); 80 ans+=MIN; 81 } 82 } 83 cout<<ans; 84 return 0; 85 }
今日专题是dp。然而笔记全记在word里了。然后word保存在邮箱里了。这里就不再写了、
以上是关于清北学堂Day3的主要内容,如果未能解决你的问题,请参考以下文章
模板tyvjP1520 树的直径 [2017年5月计划 清北学堂Day3]