【用膝盖写代码系列】(4):NOIP2014普及组复赛详解

来源:互联网 发布:专业绘画软件手机 编辑:程序博客网 时间:2024/04/29 14:42

来吧,我来渡劫了!(等下,这句话划掉)


第一题:珠心算测验

题意简述:随机生成一个正整数集合,集合中的数各不相同,然后要求学生回答:其中有多少个数,恰好等于集合中另外两个(不同的)数之和?

陷阱提示:要注意必须得不同的数,如果相同没必要记两次

数据范围:3n100,测验中每个数不大于10000

我对它的类型评估:模拟

思路描述:每一次读入数字时,将这个数字的存在设置为true,然后进行一次for循环,找有没有两个数可以加在一起等于第三个数且第三个数存在,如果有,计数,且将第三个数设置为不存在。

我的代码:

#include <cstdio>int main(){    int math[100001];    bool r[100001];    int i,j,n,m,count=0;    scanf("%d",&n);    for(i=0;i<n;i++){        scanf("%d",&math[i]);        r[math[i]] = true;    }    for(i=0;i<n-1;i++){        for(j=i+1;j<n;j++){            if(r[math[i]+math[j]]){                count++;                r[math[i]+math[j]] = false;            }        }    }    printf("%d",count);}

洛谷原题:http://www.luogu.org/problem/show?pid=2141

第一题完


第二题:比例化简

题意简述:给定一个比例A/B,以及一个上限l,使得在A’和B’均不大于L且A’和B’互质(两个整数的最大公约数是1)的前提下,
A:BA:BA:BA:B的值最小。

陷阱提示:这一道题的输入输出均用标准输入输出流(cin,cout),用scanf以及printf会炸掉

数据范围:1A1,000,0001B1,000,0001L100A/BL

我对它的类型评估:数论,模拟

思路描述:因为数据范围很小(L),所以这一道题暴力(即O(n2))可过。那么枚举两个数i,j。如果i/j小于当前的最小比例,就更新。并且必须使得i/j大于a/b。

我的代码:

#include <cstdio>#include <iostream>int main(){    double a,b,lower = 10000000,a_,b_,mid,n;    std::cin>>a>>b>>n;    mid = a/b;    for(double i=1.0;i<=n;i++){        for(double j=1.0;j<=n;j++){            if(i/j >= mid && i/j <lower-0.000000001){                a_=i;b_=j;                lower = i/j;                //printf("%f,%f\n",a_,b_);            }        }    }    std::cout<<a_<<" "<<b_;}

洛谷原题:http://www.luogu.org/problem/show?pid=2118

第二题完


第三题:螺旋矩阵

题意简述:
一个n行n列的螺旋矩阵可由如下方法生成:
从矩阵的左上角(第1行第1列)出发,初始时向右移动;如果前方是未曾经过的格子,则继续前进,否则右转;重复上述操作直至经过矩阵中所有格子。根据经过顺序,在格子中依次填入1, 2, 3, … , n,便构成了一个螺旋矩阵。给定i,j,求第i行第j列的数字。
如下,就是一个螺旋矩阵:
不可描述

陷阱提示:以这道题的数据范围来看,如果制作一个螺旋矩阵再找,会TLE

数据范围:对于100%的数据,1n30,0001in1jn

我的思路:这道题要用几个数学规律来做。我们设a[p][q]为矩阵的第p行第q列。那么我们要求出i,j的位置,就先求出它们在第n层,然后再将该层的第一个数m求出,最后计算a[i][j]是这一层的第k个数。
最后的答案就是m+k。

我的代码:

#include <cstdio>#include <cstring>int n;#define min(a,b) a<b?a:bint GetFloor(int i,int j){    int a,b;    a = min(i,n-i+1);    b = min(j,n-j+1);    return a<b?a:b;}int Get_First_Number(int a){    int count=0,p=1;    for(int i=1;i<a;i++){        count+=(n-p)*4;        p+=2;    }    return count;}int Get_Number_Of_I_And_J(int i,int j,int k){    int a = n;    for(int l=1;l<k;l++){        a-=2;    }    if(i==1) return j;    if(j==a) return a+i-1;    if(i==a) return 3*a-j-1;    else return (a-1)*4-i+2;}int main(){    int i,j;    scanf("%d%d%d",&n,&i,&j);    int floor = (i,j);    int first = Get_First_Number(floor);    int answer = Get_Number_Of_I_And_J(i-floor+1,j-floor+1,floor);    printf("%d",answer+first);}

特殊的地方:
我在玩题解的时候,看见了一个不符合常理的想法,我直接复制上来:
经计算可得
p为(x,y)所在圈的边长
nnpp为圈外数的个数
若x>y则(x,y)在圈的下或者左边,否则在上或右边。
如果在上或右边,(x(np)/2)+(y(np)/2)1=x+yn+p1就是(x,y)所在圈的排名如果在下或左边,
(2p(x(np)/2))+(2p(y(np)/2))1=3pxy+n1就是(x,y)所在圈的排名
所在圈排名加上圈外数的个数即为所求
他的代码:

#include <cstdio>#define abs(x) (x<0?-(x):x)#define max(x,y) (x>y?x:y)int n,x,y,p;int main(){    scanf("%d%d%d",&n,&x,&y);    p=max(abs(n-(x<<1)+1),abs(n-(y<<1)+1))+1;    printf("%d\n",x>y?(n-p)*(n+p+1)+(p<<2)-x-y-1:(n-p)*(n+p-1)+x+y-1);    return 0;

很短啊有木有!

洛谷原题:http://www.luogu.org/problem/show?pid=2239

第三题完


对不起,第四题我实在无能为力
抱歉。

0 0
原创粉丝点击