奋斗群群赛2总结与心得

来源:互联网 发布:iphone7 二次曝光软件 编辑:程序博客网 时间:2024/05/17 01:16

  • T1
    • 题目
    • 思路
  • T2
    • 题目
    • 思路
  • T3
    • 题目
    • 思路
  • T4
    • 题目
    • 思路
  • T5
    • 题目
    • 思路
  • 总结


https://cn.vjudge.net/contest/183263#overview

T1

题目

给出一串数字,把它们截成n段,如果有一种截法能保证n是奇数,每一段里都有奇数个数并且每一段的头尾均为奇数,则输出“Yes”,否则输出“No”.

思路

这是一道非常地道的水题,就算是像我这样的蒟蒻18分钟也能AC。思考如下:分成奇数份,每份有奇数个,总数必定是奇数。如果n是偶数可以直接输出“No”。那么当n%2==1的时候,第一个数肯定得是奇数,否则不管怎么截,一定是偶数作为第一段的第一个。同理最后一个数也得是奇数。在这样的情况下,你会发现这串数字不用截就已经符合本题的要求,1(奇数)段,奇数个,首尾奇数,直接输出“Yes”就可以了。故代码如下:没有缩进,请见谅。

#include<bits/stdc++.h>using namespace std;int a[101];int main(){int n,i;cin>>n;for (i=1;i<=n;i++) cin>>a[i]; if (n%2==0||a[1]%2==0||a[n]%2==0) cout<<"No";else cout<<"Yes";}

就是这样。

T2

题目

在平面直角坐标系中有很多点(i,a[i]),其中i=1,2,3,4,5,6,7……,a[i]由你输入。在坐标系中若能找到两条平行直线(注意:必须是两条!!!),使所有的点都在直线上,则输出“Yes”,否则输出“No”.(注意:如果所有点都在一条直线上,输出“No”.)

思路

必须要两条直线,那么在1,2,3三个点中,必有两个点的连线是符合条件的,否则就不止两条直线了。枚举三个解析式,通过一个布尔数组存储一个点是否已经被直线穿过了,然后用O(n)的四次循环爆搜,最后输出。这里请无视我布尔数组的名字,上午做题想了3个多小时没想出这道题,又订正了一下午,搞来搞去直到接近18:00才把这道题A了,实在不爽,原谅我吧。具体请看代码。我代码风格比较毒,大家可以格式化后观看。

#include<bits/stdc++.h>using namespace std;double a[1001];bool fucked[1001];//用实数定义,操作简便易行int main(){int n,i,j,x,y;double k,b,b1;cin>>n;for (i=1;i<=n;i++) scanf("%lf",&a[i]);x=1;y=2;for (i=1;i<=3;i++)  {  if (a[x]==a[y]) {k=0;b=a[x];}//具体讨论k=0,避免0作为分母  else //计算函数解析式,直接套公式    {    k=(a[y]-a[x])/(y-x);    b=(a[x]*y-a[y]*x)/(y-x);    }  for (j=1;j<=n;j++) if (k*j+b==a[j]) fucked[j]=1;//判断第一条直线穿过哪些点  for (j=1;j<=n;j++) if (fucked[j]==0) break;//找到第一个没有被穿过的点  if (j>n) {cout<<"No";return 0;}//如果j>n,说明所有点在一条直线上,输出“No”  b1=a[j]-k*j;//k相同,通过第一个没有被穿过的点的坐标找出第二条直线的b  for (j=1;j<=n;j++) if (k*j+b1==a[j]) fucked[j]=1;//第二条直线  for (j=1;j<=n;j++) if (fucked[j]==0) break;//继续找没有被穿过的点。  if (j>n) {cout<<"Yes";return 0;}//这一次说明两条直线穿过了所有点,符合题目条件,可能与上面重复了,不知道大佬们能如何优化。  for (j=1;j<=n;j++) fucked[j]=0;//初始化  if (i==1) y++;//第一次1,2,第二次1,3;第三次2,3  else if (i==2) x++;   }cout<<"No";}

T3

题目

本题是SPJ。对于一个字符串来说,一个合并所需花费是这么算的:首先把字符串的每个字符拆开,然后进行合并。对于每一次合并,将t合并到s时,对于t里的每一个字符,计算它在s里出现了多少次,把这些数字统统相加,最终的和就是所求结果。本题中输入合并的最小花费,让你输出符合该条件的一个字符串。具体看样例输入输出的下面样例解释。

思路

这题是spj,当然怎么简单怎么来了。首先我们可以发现,在合并时不同种类字符不互相影响,所有的花费对于每个字母来说都是独立的,并且对于相同数量的同种字母来说,不管怎么合并,所需的花费都是一样的,并且合并的花费S和字母数量n满足S=(n*n-n)/2;
因此有如下代码。我先找很多a,算出它们合并的花费,用一个过程找出最多能合并多少a,然后减掉合并a所用的花费,用剩下的来算b,c……一路推上去,直到n=0为止。

#include<bits/stdc++.h>using namespace std;void digui(int n,char c){int i=2,j;if (n==0) return;while ((i*i-i)/2<=n) i++;for (j=1;j<=i-1;j++) printf("%c",c);i--;digui(n-(i*i-i)/2,c+1);}int main(){int n;cin>>n;digui(n,'a');}

这个代码提交上去WA了。为什么WA?我在这里强调一点:特判!特判!特判!重要的事情一定要说三遍!当时这个问题因为输入n=0时没有特判坑了我三个小时,最后我还是没有A这题。我一直搞到晚上有人提醒我没有特判0。我加上了0的特判,并且优化了代码:

#include<bits/stdc++.h>using namespace std;void digui(int n,char c){  int i=sqrt(n),j;//这里i=sqrt(n)是一个优化,  if (n==0) return;  while ((i*i-i)/2<=n) i++;//在这句当中这里i只需要加一点就能停止循环  for (j=1; j<=i-1; j++) printf("%c",c);  i--;  digui(n-(i*i-i)/2,c+1);}int main(){  int n;  cin>>n;  if (n==0) cout<<"a";//不要忘记特判!  digui(n,'a');}

然后就A了。这题也坑了我一天。

T4

题目

看到这个题目我直接被搞晕了,以为这是我并没有学过的向量。这只是一道字符串找规律题。输入n,输出2^n*2^n的正方形方阵,每一个元素都是“+”或者“*”,+代表+1,*代表-1。对于每两行来讲,它们当中同位置不同字符的数量刚好等于该行字符数的一半。(我往简单了讲)只要输出符合条件的一个方阵即可。

思路

最终我还是找到了规律,对于每一个正方形,分成四个角递归,左上,右上,左下的元素完全相同,右下的所有元素均与其他三个方向相反。具体请看代码。这个代码输入9的时候要18秒才能出结果,我怕超时,但是还是提交了,没想到A了。我用一个bool类型的变量控制每一块的字符是相同还是相反。

#include<bits/stdc++.h>using namespace std;char c[513][513];void search(int x1,int y1,int x2,int y2,bool b)//递归左上角坐标,右下角坐标,判定正负变量{if (x1>=x2||y1>=y2) return;if (x2-x1==1&&y2-y1==1)   {  if (b==1)     {    c[x1][y1]='+';    c[x1][y2]='+';    c[x2][y1]='+';    c[x2][y2]='*';    }  else     {    c[x1][y1]='*';    c[x1][y2]='*';    c[x2][y1]='*';    c[x2][y2]='+';    }  }else //当时提交之前下面这一块+1错了,输入2以上的数字就会崩掉,后来改了一下A了  {  search(x1,y1,(x1+x2)/2,(y1+y2)/2,b);//递归左上角  search((x1+x2)/2+1,y1,x2,(y1+y2)/2,b);//递归左下角  search(x1,(y1+y2)/2+1,(x1+x2)/2,y2,b);//递归右上角  search((x1+x2)/2+1,(y1+y2)/2+1,x2,y2,!b);//递归右下角  }}int main(){int n,i,j;cin>>n;if (n==0) printf("+");else  {  search(1,1,1<<n,1<<n,true);  for (i=1;i<=1<<n;i++)    {    for (j=1;j<=1<<n;j++) printf("%c",c[i][j]);    printf("\n");    }  }}

T5

题目

输入一串数据,令f(n)=在这串数据中能被n整除的数据的个数(n是质数),以下m行每行两个数字,询问从l至r所有质数的f函数之和.

思路

我写了一个O(n^2)的代码,因为吓人的数据范围在第18个点T了。具体就是算出f[i]之后用一个sum数组来储存i之前的数字的函数返回值的和,询问时只要求sum[r[i]]-sum[l[i]]即可。还有求质数的时候一定要用筛法!否则时间复杂度太高铁定会T!代码如下,今天搞了一下午用最大的数据直接暴力搞定了.可以定义一个常数等于10000005.

#include<bits/stdc++.h>using namespace std;int a[1000010],f[10000010]={0},l,r,tong[10000010]={0};bool p[10000010];int main(){int n,m,i,j,da=-100000;cin>>n;for (i=1;i<=n;i++)  {  scanf("%d",&a[i]);  tong[a[i]]++;//利用桶来做这题  }p[1]=1;for (i=2;i<=10000005;i++) if (p[i]==0)  {  for (j=i;j<=10000005;j+=i)    {    p[j]=1;    f[i]+=tong[j];//j一定是i的倍数,用这样的方法不需要再从1到n循环一遍,就不会超时了    }    }for (i=2;i<=10000005;i++) f[i]+=f[i-1];//在计算询问的最终结果的时候也不用一遍一遍循环cin>>m;for (i=1;i<=m;i++)  {  scanf("%d%d",&l,&r);  if (l!=10000005) l=min(l,10000004);  r=min(r,10000004);  printf("%d\n",f[r]-f[l-1]);//f这个数组表示的是数据i及之前的所有数据的函数和  } }

总结

总体来说这5题还是比较好懂的,至少我全读懂了,会不会做是另外一回事,反正我能订正问题不大。我在现场3小时30分钟之内A了第一题和第四题,依然做得非常糟糕。总之还好啦.

原创粉丝点击