蓝桥杯刷题【23.11.10-23.11.12】

来源:牛客网

1030-Game

Nancy喜欢博弈!
Johnson和Nancy得到了一个神奇的多重集合,仅包含一个正整数n,两个人轮流进行操作。
一次操作可以将集合中一个数字分解为它的任意两个非1的因数,并加入集合中。
他们想知道,在Johnson和Nancy绝顶聪明的情况下,如果Nancy先手进行操作,最后谁没有办法继续操作了呢?

输入描述:
第一行:一个整数n。
数据满足:1≤n≤957181

输出描述:
共一行:一个字符串,表示最后谁(Johnson或者Nancy)无法进行操作。 

解题思路:题目实质是求解一个数可以分解成几个素数的乘积

1.一个合数一定可以分解成有限个素数的乘积---唯一分解定理

#include <iostream>
using namespace std;

int main(){
    int n;
    cin>>n;
    
    int cnt = 0;//统计n可以拆分成多少个素数
    
    for(int i = 2; i * i <= n; i ++ ){
        while(n%i==0){
            cnt ++;
            n/=i;
        }
    }
    if(n!=1) cnt ++;
    if(cnt%2==0) cout<<"Johnson"<<endl;
    else cout<<"Nancy"<<endl;
    
    return 0;
}

2.n/=i使得n一直在变化,是否会影响i*i<n这个判定条件?

??????????

3.正常情况下,应该是从小到大一次除以素数,为什么在循环中没有类似判定?

正常应该是2,3,5,7… 但若是除6,因为前面已经除了2,3,所以就已经不会在产生素数因子,且可以省略素数的判别过程

4.为什么最后还要判定n!=1?

如果n不等于1,那么当前的值也是n的素数因子,需要对其进行统计。

[NOIP1999]Cantor表

现代数学的著名证明之一是Georg Cantor证明了有理数是可枚举的。他是用下面这一张表来证明这一命题的:

 我们以Z字形给上表的每一项编号。第一项是1/1,然后是1/2,2/1,3/1,2/2,…

输入描述:
整数N(1≤N≤10000000)

输出描述:
表中的第N项

解题思路:

1.找规律,先确定n是属于第几列,再根据奇偶列分别分析

#include <iostream>
using namespace std;

int main(){
    
    int n;
    cin>>n;
    //先确定n是在第几列
    int i;
    for(i = 1; n - i > 0; i ++ ){
        n-=i;
    }

//     另一个判断n在第几列的写法:本质上一样的
//     int i = 1;
//     while((1+i)*i/2 < n){
//         i ++;
//     }
//     n -= (1+i)*i/2 - i;

    if(i%2==0){
        printf("%d/%d\n", n, i + 1 - n);
    }else{
        printf("%d/%d\n", i + 1 - n, n);
    }
    
    return 0;
}

糖果俱乐部

中国地质大学(武汉)坐落于南望山下毗邻东湖风景区。为了庆祝“华为杯”中国地质大学(武汉)第十七届ICPC程序设计大赛暨华中地区部分高校第十五届ICPC邀请赛竞赛的举办,校园中开展了许多有趣的热身小活动。 HW听到这个消息非常激动,他赶忙去参加了糖果俱乐部的活动。该活动的规则是这样的:摊位上有 n 堆糖果,第 i 堆糖果有ai 个,参与的同学可以选择其中的任意堆,当所选择糖果的总数为偶数就可以把它们都带走啦~试问通过这个活动小 HW 最多能在摊位上带走多少枚糖果。

输入描述:
第1行 输入一个整数 n(n≤100)\ n (n \leq 100) n(n≤100),代表共有 n\ n n堆糖果。 

第2行 依次输入 n\ n n个整数aia_iai​ ( ai≤100000)(\ a_i \leq 100000)( ai​≤100000),代表每堆糖果的数量。

输出描述:
输出一个整数,代表小 HW, HW最多能取得的糖果。

示例1

输入
4

1 2 3 4

输出
10

示例2

输入
8

10 11 10 10 10 10 10 10

输出
70

解题思路:1.偶数是可以全部带走的,但若存在3个奇数组,则应该是留下个数最少的组。

#include <iostream>
using namespace std;

const int N = 110;
int a[N];

int main()
{
    int n;
    cin>>n;
    int cnt = 0;
    int oddnum = 0;
    int minodd = 100010;
    for(int i = 1; i <= n; i ++ )
    {
        cin>>a[i];
        cnt += a[i];
        if(a[i]%2){
            oddnum ++;
            minodd = min(minodd, a[i]);
        }
    }
    
    if(oddnum%2) cnt -= minodd;
    
    cout<<cnt<<endl;
    return 0;
}

2.刚开始存在误区,只考虑了两个奇数可以拼凑成偶数带走,忘了考虑需要带走最大的奇数。

3.小优化:其实也没有必要存储在数组中,只需要记录最小的奇数就可以了

焦虑的蚂蚁
        一条长度为n的小径上,挤满了m只蚂蚁,每只蚂蚁有一个初始前进方向(向左或是向右),蚂蚁们的前进速度相同且均为1。小径的两端,均放着上了锁的箱子,箱内盛有美味的食物,蚂蚁们争相前进离开小径。不幸的是,小径十分狭窄,当两只蚂蚁相遇时,它们不得不掉头向着相反的方向前进。当所有的蚂蚁都离开小径时,锁才能够打开,蚂蚁们才能获得美味的食物。因此,蚂蚁们十分焦虑,它们想知道,到底花费多少的时间,它们才能够吃到美味的食物。你能够帮助他们解决问题吗?

输入描述:
第一行,2个整数n,m(1≤m<n≤100000,n,m均为整数),分别代表小径的长度,蚂蚁的数量。

接下来m行,每行2个数,分别代表蚂蚁的初始方向(0代表向左,1代表向右),蚂蚁的初始坐标x满足x为整数且0<x<n,蚂蚁走到坐标0或坐标n即代表离开小径。

数据保证每个坐标上最多有1只蚂蚁。数据保证给出的蚂蚁初始坐标为从小到大。

输出描述:
一行,对结果四舍五入,输出1个整数,代表蚂蚁吃到美味的食物的时间。

解题思路:

1.在碰撞前,是一只蚂蚁向左,一只蚂蚁向右,碰撞后也一只蚂蚁向左,一只蚂蚁向右。因此可以想象成两只蚂蚁灵魂互换,但身体不改变方向,也就是假设已经交换了方向,所以其实求的是所有蚂蚁走向两端的最大时间。

#include <iostream>
using namespace std;

int main(){
    int len, n;
    
    cin>>len>>n;
    int ans = 0;
    for(int i = 0; i < n; i ++ ){
        int a, dire;
        cin>>dire>>a;
        if(dire==1){
            a = len - a;
        }
        ans = max(a, ans);
    }
    cout<<ans<<endl;
    return 0;
}

2.当方向==1时,也就代表向右走,所以走的长度应该是len-a


HtBest的小鲲长大变成了大鹏,大鹏在天际翱翔,看到了一片绵延的山脉,每座山都有自己的高度,大鹏想穿过这片山脉。由于他只能紧贴地面飞行,他想知道他一共要翻越几次大山(上升->平飞->下降,算一次,其中平飞可以没有),初始时,大鹏在山脉的左端。

输入描述:
第一行一个正整数n,表示山脉被分为n段。
第二行有n个正整数ai两两之间用空格分开,ai表示山脉第i段的高度。

输出描述:
一行,包含一个正整数,表示大鹏需要翻越几次大山。

示例1
3
1 2 1 
输出
1
说明
大鹏先上升一次,再下降一次,共翻越1次。  
示例2
输入
3
3 1 2
输出
0
说明
大鹏先下降一次,再上升一次,共翻越0次。  
解题思路:

1.只需要在每次出现下降的时候判断前面是否是上升即可

#include <iostream>
using namespace std;

int main(){
    int n;
    cin>>n;
    bool isascend = false;
    int cnt = 0;
    int a, b;
    cin>>a;
    for(int i = 1; i < n; i ++ ){
        scanf("%d", &b);
        if(b > a) isascend = true;
        else if(b < a){
            if(isascend) cnt ++;
            isascend = false;
        }
        a = b;
    }
    cout<<cnt<<endl;
    return 0;
}

2.用一个bool值记录前面是否出现上升,判断上升还是下降其实只需要两个山峰的高度即可,可以减少变量的浪费,若已经统计一次下降,记得将bool恢复

更相减损术

#include <iostream>
using namespace std;

int main(){
    int a, b;
    cin>>a>>b;
    if(b > a) swap(a,b);
    while(b!=0){
        a = a - b;
        if(b > a) swap(a,b);
    }
    cout<<a<<endl;
    return 0;
}

求解两个数的最大公约数,每次都用较大的数减去较小的数,直到较小的数为0时,较大的数就是它们的最大公约数。

[NOIP2009]多项式输出

#include <iostream>
using namespace std;

int main()
{
    int n;
    cin>>n;
    
    for(int i = n; i >= 0; i --){
        int t;
        cin>>t;
        //先符号位
        if(t > 0 && i != n) cout<<"+"; 
        //再系数
        if(t!=0){
            if(t == 1){
                if(i == 0) cout<<"1";
            }
            else if(t == -1) {
                if(i == 0) cout<<"-1";
                else cout<<"-";
            }
            else cout<<t;
        
            if(i == 1) cout<<"x";
            else if(i == 0){}
            else cout<<"x^"<<i;
        }
    }
    
    return 0;
}

解题思路:

1.题目需要考虑的分支情况比较多,容易出错

1.1 如果非最高次项系数为正,需要添加正号

1.2一次项只需要x,其他次项需要x^4类似

1.3+1和-1如果是常数项需要输出,如果不是,仅需要输出符号位

博弈与核心能源动力
ZWY最近喜欢在下课后喝酒,她说:“喝酒是人类进步的动力”。现在,便利商店推出了兑换活动。
    1.两个酒瓶可以兑换1瓶酒
    2.四个酒瓶盖子可以兑换1瓶酒
她先知道,在当前她所拥有的资金m和一些空酒瓶k、瓶盖g和当前酒价p已知情况下,她最多可以喝到多少瓶酒。

#include <iostream>
using namespace std;

int main(){
    int ans = 0;
    
    int m, k, g, p;
    cin>>m>>k>>g>>p;
    
    if(m > 0){
        int t = m / p;
        k += t;
        g += t;
        ans += t;
    }
    
    while(k >= 2 || g >= 4){
        if(k >= 2){
            int t = k / 2;
            ans += t;
            k = k % 2 + t;
            g += t;
        }
        if(g >= 4){
            int t = g / 4;
            ans += t;
            k += t;
            g = g % 4 + t;
        }
    }
    
    cout<<ans<<endl;
    
    return 0;
}

栗酱的文明2

已知游戏结束前场上有n个国家,第i个国家有ai块土地,任意2个国家若是想建立外交关系,则需要互相在对方的一块土地上建立一个大使馆。
一块土地只能建立一个大使馆,若一个国家和其他国家存在外交关系,则需要征用一块己方土地作为备用大使馆。
完美结局的定义是:找到最多数量的国家,使他们相互之间存在外交关系。

#include <iostream>
#include <algorithm>
using namespace std;
int a[1010];
int main(){
    
    int T;
    cin>>T;
    while(T --){
        int n;
        cin>>n;
        for(int i = 1; i <= n; i ++ ){
            cin>>a[i];
        }
        sort(a + 1, a + n + 1, greater<int>());
        int ans = 0;
        for(int i = 1; i <= n; i ++ ){
            if(a[i]<i) break;
            ans = i;
            
        }
        cout<<ans<<endl;
    }
    
    return 0;
}

解题思路:

1.将数组从大到小排列,只要第i个国家的土地个数大于等于i,也就代表满足建立大使馆的条件

[NOIP2006]明明的随机数
明明想在学校中请一些同学一起做一项问卷调查,为了实验的客观性,他先用计算机生成了N个1到1000之间的随机整数(N ≤ 100),对于其中重复的数字,只保留一个,把其余相同的数去掉,不同的数对应着不同的学生的学号。然后再把这些数从小到大排序,按照排好的顺序去找同学做调查。请你协助明明完成“去重”与“排序”的工作。

解题思路:先去重后排序

#include <iostream>
#include <algorithm>
using namespace std;

int a[100];

int main(){
    int n;
    cin>>n;
    int m = 0;
    int x;
    //输入+去重
    for(int i = 0; i < n; i ++ ){
        cin>>x;
        bool flag = false;
        for(int j = 0; j < m; j ++){
            if(a[j] == x){
                flag = true;
                break;
            }
        }
        if(!flag) a[m ++] = x;
    }
    //选择排序
//     for(int i = 0; i <= m - 2; i ++ ){
//         int min_id = i;
//         for(int j = i + 1; j < m; j ++ ){
//             if(a[j] < a[min_id]) min_id = j;
//         }
//         swap(a[i],a[min_id]);
//     }
    
    sort(a, a + m);
    
    cout<<m<<endl;
    for(int i = 0; i < m; i ++ ){
        cout<<a[i]<<" ";
    }
    
    return 0;
}

2.先排序后去重

对每个数都看它前面的数是否相同,如果相等,那么这个数就可以删掉

#include <iostream>
#include <algorithm>
using namespace std;

int a[100];

int main(){
    int n;
    cin>>n;
    for(int i = 0; i < n; i ++ ){
        cin>>a[i];
    }
    
    sort(a, a + n);
    
    int m = 1;
    for(int i = 1; i < n; i ++ ){
        if(a[i]==a[i-1]) continue;
        else{
            a[m ++] = a[i];
        }
    }
    
    cout<<m<<endl;
    for(int i = 0; i < m; i ++ ){
        cout<<a[i]<<" ";
    }
    
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值