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

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


第一题:珠心算测验

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

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

数据范围: 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:B A:BA:B 的值最小。

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

数据范围: 1A1,000,000 1B1,000,000 1L100 A/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,000 1in 1jn

我的思路:这道题要用几个数学规律来做。我们设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:b
int 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
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值