T1 消失的数字(number)
Time Limit:1000ms Memory Limit:128MB
题目描述
rsy拥有n个数,这n个数分别是a1,a2,…,an。
后来出现了一个熊孩子zhw,用橡皮擦去了其中若干个数字,并且打乱了剩下的数字。rsy赶到现场后只剩下了m个数字b1,b2,…,bm,她想知道哪些数字被擦去了。
现在你需要告诉rsy被擦去的n-m个数是什么。
输入格式(number.in)
第一行一个数n,第二行n个数ai,表示一开始的数字。
第三行一个数m,第四行m个数bi,表示被擦去后的数字。
输出格式(number.out)
一行n-m个数,从小到大输出所有被擦去的数字。
输入样例
5
1 3 5 7 8
3
3 5 8
输出样例
1 7
数据范围
对于30%的数据n<=1000,ai与bi都是有序的。
对于60%的数据n<=100000,ai与bi都是有序的。
对于80%的数据n<=100000,ai,bi<=n。
对于100%的数据n<=100000,1<=ai,bi<=10^9。
(T1)一看题目,这多简单,数据100000明显nlog(n)的解法嘛,然后自然想到二分,感觉没问题了,结果数据太强大(没考虑全面,没想到还有相同的数字)考完试后看完标程,恍然大悟,一看这么简单啊,不应该啊!!
解:将第二数列中的数从第一个数列中删除(不输出就好了),排两遍顺俩数列就都是单调的了,增减看题目,第二个数列下标从1开始碰上与的一个数列有相同的数就不输出第一个数列中的那个数(相当于删除了),按顺序输出就好了.(自己zz了)
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; int n,m; int a[100001],b[100001]; int read() { int sum=0,fg=1; char c=getchar(); while(c>'9'||c<'0'){if(c=='-')fg=-1;c=getchar();} while(c<='9'&&c>='0'){sum=sum*10+c-'0';c=getchar();} return fg*sum; } int main() { // freopen("number.in","r",stdin);freopen("number.out","w",stdout); n=read(); for(int i=1;i<=n;i++)a[i]=read(); sort(a+1,a+1+n); m=read(); for(int i=1;i<=m;i++)b[i]=read(); sort(b+1,b+1+m); int k=1; for(int i=1;i<=n;i++) { if(a[i]==b[k])k++; else cout<<a[i]<<" "; } // fclose(stdin);fclose(stdout); }
T2 一道数论好题(math)
Time Limit:1000ms Memory Limit:128MB
题目描述
rsy最近在研究欧几里得算法,不会的同学可以去看下课件以及代码……
现在她想到了一个新的问题,求k个数的最大公约数!
事实上这个问题仍然很简单。所以rsy想强化一下,她觉得最大公约数等于1就不是很有趣了。因此她想在n个数中找最多的数,使得它们的最大公约数不等于1。
现在rsy想知道,能找最多多少个数。
输入格式(math.in)
第一行一个数n。
第二行n个数ai。
输出格式(math.out)
一个数表示答案。
输入样例
5
2 3 4 5 6
输出样例
3
数据范围
对于30%的数据n<=5。
对于50%的数据n<=20。
对于80%的数据n<=1000,ai<=1000。
对于100%的数据1<=n<=100000,1<=ai<=100000,数据几乎是随机的。
一看求最大公约数,正解,不会,求个gcd吧,数据这么大,这咋挑数呢?没办法写个dfs,再优化优化,应该大约可以过个50%的数据吧,
结果只过了30%。
30%--直接dfs;
50%-80%--桶排的话应该可以过,数的大小<=1000,比较密集。
100%,看起来既得部分分还简单,开始记录一下每一个数出现的次数,枚举这一堆数的最大公约数,既然这些数最大公约数不唯一那另外的一些数一定是这个约数的倍数,二重循环记录每个数的个数总和,比较得出一个max就可以啦。
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; int n,m,ans,maxn; int a[2000001]; int read() { int sum=0,fg=1; char c=getchar(); while(c>'9'||c<'0'){if(c=='-')fg=-1;c=getchar();} while(c<='9'&&c>='0'){sum=sum*10+c-'0';c=getchar();} return fg*sum; } int main() { // freopen("math.in","r",stdin);freopen("math.out","w",stdout); n=read(); int x; for(int i=1;i<=n;i++) { x=read(); a[x]++; maxn=max(maxn,x); } for(int i=2;i<=maxn;i++) { int sum=0; for(int j=i;j<=maxn;j+=i) { sum+=a[j]; } ans=max(ans,sum); } printf("%d",ans); // fclose(stdin);fclose(stdout); }
T3 扫雷(mine)
Time Limit:1000ms Memory Limit:128MB
题目描述
rsy最近沉迷于一款叫扫雷的游戏。
这个游戏是这样的。一开始网格上有n*m个位置,其中有一些位置有雷。每次rsy可以左键点击一个方块,此时若这个方块是雷,则rsy被炸,游戏结束,否则如果这个位置周围8格有x个雷,则会显示数字x。特别地,当x=0时,系统会自动左键点击附近8个位置。(此时附近8个位置一定没有雷,假如附近8个位置仍存在x=0,则继续往外扩展)想要更进一步获得关于题目的信息,打开程序->附近->游戏->扫雷或者直接打开下发的可执行文件。
或者rsy右键点击一个位置,标注这个位置是雷。
不幸的是,她鼠标不能左右键同时点击,因此只需考虑她左键点击与右键点击就可以了。
注意游戏胜利的条件是所有非雷的位置都被左键点击到。(特别地,当一开始时n*m个位置都是雷时,LYK自动获得胜利)
rsy从网上下载了金手指,很轻易地就掌握了所有雷所在的位置。rsy想通过最少的点击次数获得胜利(这里的点击次数不包括系统自动点击)。于是他来请求你的帮助。
输入格式(mine.in)
第一行两个数n,m。
接下来n行,每行m个数ai,j,表示这个矩阵。若ai,j=’*’则表示这个位置是雷,若ai,j=’.’则表示不是雷。
输出格式(mine.out)
一个数表示答案。
输入样例
3 3
..*
...
..*
输出样例
2
对于30%的数据n=1;
对于另外20%的数据n,m<=3;
对于再另外20%的数据*大致占矩阵的2/3且数据随机。
对于100%的数据n,m<=1000。
Hint:
适度游戏益脑,沉迷游戏伤身。
解:既然可以自动扩展,明显bfs啊,又要点击次数最小,那肯定要先点可以扩展的绝对比点不能扩展的更优,开个数组记录下,两重循环bfs,扩展到的记录下来,最后图中还有一些个点没有被标记过并且不是雷的需要单点,每点一个次数加1,加完以后就是最少的次数了。so easy!
#include<iostream> #include<cstdio> #include<algorithm> #include<queue> using namespace std; int n,m,ans; int dx[9]={0,0,1,1,1,-1,-1,-1}; int dy[9]={-1,1,0,-1,1,0,-1,1}; char a[1005][1005]; //数组[1001]完蛋 int map[1005][1005]; struct ahah{ int x,y; }cur,str,kpl; int read() { int sum=0,fg=1; char c=getchar(); while(c>'9'||c<'0') { if(c=='-')fg=-1; c=getchar(); } while(c<='9'&&c>='0') { sum=sum*10+c-'0'; c=getchar(); } return fg*sum; } queue <ahah> que; bool vis[1005][1005]; void bfs(int x,int y) { kpl.x=x;kpl.y=y; vis[x][y]=1; que.push(kpl); while(!que.empty()) { str=que.front(); que.pop(); for(int i=0;i<8;i++) { cur.x=str.x+dx[i]; cur.y=str.y+dy[i]; if(cur.x>=1&&cur.x<=n&&cur.y>=1&&cur.y<=m&&!vis[cur.x][cur.y]&&a[cur.x][cur.y]!='*') { vis[cur.x][cur.y]=1; if(map[cur.x][cur.y]==0)que.push(cur); } } } } int main() { // freopen("mine.in","r",stdin);freopen("mine.out","w",stdout); n=read();m=read(); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { cin>>a[i][j]; if(a[i][j]=='*') { map[i][j-1]++; map[i][j+1]++; map[i+1][j]++; map[i-1][j]++; map[i+1][j+1]++; map[i+1][j-1]++; map[i-1][j+1]++; map[i-1][j-1]++; } } } for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if(map[i][j]==0&&!vis[i][j]&&a[i][j]!='*') { bfs(i,j); ans++; } } } for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if(!vis[i][j]&&a[i][j]!='*')ans++; } } printf("%d",ans); // fclose(stdin);fclose(stdout); } //附上组数据 /* 输入: 1 16 ...*....*.*.*.*. 输出:6 */
总结:
对于这类模拟来说,越简单就越容易忽视一些细节,做题不细心,题目中的条件要看全面,数组不能卡数据大小尽量多大个一二百的,做题思路要开阔避免惯性思维,开阔思路。
到的记录下来,最后图中还有一些个点没有被标记过并且不是雷的需要单点,每点一个次数加1,加完以后就是最少的次数了。