南昌大学航天杯第二届程序设计竞赛校赛

本文精选了多道经典算法题目,涵盖博弈论、模拟题、数论、数组划分等多个方面,通过详细解析帮助读者理解每道题目的解题思路及代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

A.大小写转换

水题,不谈。

 

 

B.取石子

题目描述

现在有两堆石子,两个人轮流从中取石子,且每个人每一次只能取1、3或9个石子,取到最后一个石子的人win。 假设先手后手都会选择最好的方式来取石子,请您判断先后手的输赢情况。
输入描述:
多组输入每组一行,一行包括两个正整数n1和n2(1<=n1<=100,1<=n2<=100),代表了两堆石子的数目
输出描述:
如果先手能赢,输出"win";否则就输出"lose"。

示例1

输入

1 1
1 2

输出

lose
win

思路:博弈的架势,被吓了一跳,可惜找规律就能a。

代码如下:

#include<stdio.h>
int main()
{
    int a,b,c;
    while(scanf("%d%d",&a,&b)!=EOF)
    {
        a+=b;
        if(a%2==0)
        printf("lose\n");
        else
        printf("win\n");
    }
    return 0;
}

C.水题

题目描述

 一张地图上有有N个城市,他们可以通过双向道路互相连接,但是每两座城市只能有一条双向道路互相连接。    现在我们想要满足条件“地图中不能有任意三个城市可以互相直达”,请问满足这个条件的最大道路数是多少?
输入描述:
多组输入每组输入一个N(1<=N<=1000)
输出描述:
每组答案输出一行

示例1

输入

4
2
3

输出

4
1
2

 

 

 

思路:·····按经验来看自称水题的题都不可能是水题,结果是一道找规律·····取2的余数判断。

代码如下:

#include<stdio.h>
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        if(n%2==0)
        printf("%d\n",n*n/4);
        else
        printf("%d\n",((n-1)/2)*((n-1)/2)+(n-1)/2);
    }
    return 0;
}

 

D.小c的笔记本

题目描述

小C最近学会了java小程序的开发,他很开心,于是想做一个简单的记事本程序练练手。  他希望他的记事本包含以下功能:  1、append(str),向记事本插入字符串 str(英文字符)  2、delete(k),删除记事本最后k个字符(保证不为空串)  3、print(k),输出记事本第k个字符(保证不为空串)  4、undo(),撤销最近的1(或者)操作,使记事本回到1(或者2)操作之前的状态  可怜的小C琢磨了半天还是做不来,聪明的你能解决小C的问题吗?
输入描述:
多组输入第一行输入一个整数q,代表操作总数以下q行每行描述了一个操作,每行以一个整数t开始(1 <= t <= 4)。t表示上述问题陈述中定义的操作类型。 如果操作需要参数,则后跟空格分隔的参数。题目保证所有操作均合法1 <= q <= 10^6 1 <= k <= |记事本内容长度| 每个测试数据中str的总长度 <= 10^6请使用 ios::sync_with_stdio(false); 对读写进行加速
输出描述:
所有操作类型3必须输出第k个字符,每行以换行符结束。

示例1

输入

8
1 ab
3 2
2 2
1 cd
3 1
4
4
3 1

输出

b
c
a

说明

**样例解释**假设记事本用字符串S表示1、插入ab,S="ab"2、输出第2个字符,是b3、删除最后2个字符,S=""4、插入cd, S="cd"5、输出第1个字符,是c6、撤销,此时S=""7、撤销,此时S="ab"8、输出第1个字符,是a

思路:赤裸裸的模拟题。

代码如下:

#include <iostream>
#include <string>
#include <queue>
#include <stack>
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    int i,n,m;
    while (cin>>n)
    {
        stack<string> a;
        string str="",t;
        for (int q=0;q<n;q++)
        {
            cin>>i;
        if (i==1)
        {
            cin>>t;
            a.push(str);
            str=str+t;
        }
        else if (i==2)
        {   
            cin>>m;
            a.push(str);
            str.erase(str.size()-m,str.size());
        }
        else if (i==3)
        {
            cin>>m;
            printf("%c\n",str[m-1]);
        }
        else if (i==4)
        {
            str=a.top();
            a.pop();
        }
        }
    }
    return 0;
}

 

E.小c的周末

题目描述

愉快的周末到了,小C和他的N-1个朋友买了M个游戏,游戏编号从1~M。每个游戏都是多人游戏,他们打算周末一起打游戏。  小C的每个朋友都决定好了要玩哪一款游戏(会有一组人打同一款游戏),并且每人手上都有一台游戏机,这种游戏机可以通过特定的游戏机连接线连接起来。  但是,他们面临着一个问题:目前没有一个朋友的游戏机是互相连接的。所以它们必须用可用的游戏机连接线连接起来。小C决定依次使用第 i 条连接线把他的朋友 ui 和 vi 的游戏机连接起来。也就是说,假设有Q条连接线,小C只能先使用第一条,然后使用第二条,然后使用第三条。。。最后使用第Q条。  一个游戏能开始的条件是所有玩这个游戏的朋友的游戏机都被连接起来(如果不是直接连接的话,那么就必须存在一条连接它们的路径)。他们希望尽快开始比赛。  在每个游戏中,找出在添加了第几条连接线之后能开始游戏。如果在一个游戏中只有一个人玩,则输出0(因为他立马可以开始游戏)。如果不存在,则输出-1
输入描述:
多组输入第一行包含三个整数N,M,Q。第二行给N个用空格分隔的整数,第 i 个整数代表第 i 个朋友想玩的游戏。接下来的Q行,每行两个整数(u, v),代表电线 i 连接的两个人的电脑1 <= N, M <= 10^50 <= Q <= 10^5
输出描述:
对于每个游戏,输出一个整数,表示添加了第几条连接线之后能开始游戏,每行以换行符结束

示例1

输入

5 2 4
1 2 2 2 1
1 2
2 3
1 5
4 5

输出

3
4

思路:本想用搜索或并查集试试,结果炸了,以下为大佬代码。

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+55;
int cnt[maxn],x,n,m,q;
int f[maxn];
map<int,int> mp[maxn];
int res[maxn];
int find(int x){
    if(x == f[x]) return x;
    return f[x] = find(f[x]);
}
int main () {
    while(~scanf("%d%d%d",&n,&m,&q)) {
        memset(cnt,0,sizeof cnt);
        memset(res,-1,sizeof res);
        for(int i=1;i<=n;i++)
            mp[i].clear();
        for(int i=1;i<=n;i++){
            scanf("%d",&x);
            cnt[x]++;
            mp[i][x]++;
        }
        for(int i=1;i<=n;i++)
            f[i]=i;
        for(int i=1;i<=q;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            int h1 = find(u);
            int h2 = find(v);
            if(h1 == h2) continue;
            if(mp[h1].size() > mp[h2].size()){
                for(auto ep:mp[h2]) {
                    int tmp = (mp[h1][ep.first] += ep.second);
                    if(tmp == cnt[ep.first]) {
                        res[ep.first]=i;
                        mp[h1].erase(ep.first);
                    }
                }
                f[h2]=h1;
            } else {
                for(auto ep:mp[h1]) {
                    int tmp = (mp[h2][ep.first] += ep.second);
                    if(tmp == cnt[ep.first]) {
                        res[ep.first]=i;
                        mp[h2].erase(ep.first);
                    }
                }
                f[h1]=h2;
            }
        }
        for(int i=1;i<=m;i++){
            if(cnt[i]==1){
                printf("0\n");
            } else {
                printf("%d\n",res[i]);
            }
        }
    }
    return 0;
}

 

F.阿汤的疑惑

题目描述 
阿汤同学最近刚学数论,他发现数论实在是太有趣了,于是他想让你也感受一下数论的乐趣。现在他给你一个正整数 N 和一个正整数 M,要求你用 N 对 M 进行取余操作,即 N % M,记余数为 S。
但是他发现这样好像并不能让你感受到数论的乐趣,于是他想让你在N 对 M 取余操作的基础上再求出这个余数 S 能分解出多少个不同质因数。

质因数:质因数在数论里是指能整除给定正整数的质数,质数就是只能整除 1 和本身的数,定义 2 是最小的质数。
输入描述:
从标准输入读入数据。
输入包含多组数据,第一行一个整数 T 代表数据组数。接下来依
次描述每组数据,对于每组数据:
第一行输入正整数 N,第二行输入正整数 M

【数据规模】
1≤N≤10^100
1≤M≤2^31-1
输出描述:
输出到标准输出。
对于每组数据,输出一行:
余数 S 能分解出的不同质因数的个数。
示例1
输入
2
68
40
6
180
输出
2
2

思路:看看数据范围,暴力好像没得想,打表好像也不行,结果,,,人家就是暴了。。。

代码如下:

#include <iostream>
#include <string>
#include <queue>
using namespace std;
int main()
{
    string str;
    long long i,n,m;
    scanf("%d",&i);
    while (i--)
    {
        cin>>str>>n;
        m=0;
        for (int q=0;q<str.length();q++)
        {
            m=(m*10+str[q]-'0')%n;  
        }   
        int num=0;
        for (int q=2;q*q<=m;q++)
        {
            if (m%q==0)
            {
                num++;
                while (m%q==0)
                m/=q;
            }
       }
        if (m!=1) num++;
        cout<<num<<'\n';
    }
    return 0;
}

 

G.阿汤的数组

题目描述

阿汤同学为了准备下学期的 ACM-ICPC,刷了很多的题目,他觉得自己已经比较厉害了,于是想出个题目考考你。现在他给你一个数组 A,问你是否能将该数组划分成数组 B、C 使得 B 数组的平均数和C 数组的平均数相等,数组 B 和 C 都不能为空。

输入描述:

从标准输入读入数据。
输入包含多组数据,第一行一个整数 T 代表数据组数。接下来依次描述每组数据,对于每组数据:
第一行输入正整数 N,第二行输入 N 个非负整数

1≤|A|≤30 (数组 A 的长度范围在 1 到 30 之间 )
0≤A[i]≤10000 (数组 A 中的元素)

输出描述:

输出到标准输出。
对于每组数据,输出一行:
如果能划分成满足题目要求的数组 B 和 C 则输出 yes,否则输出no

示例1

输入

1
8
1 2 3 4 5 6 7 8

输出

yes

说明

样例说明:将数组 A 划分成【1,4,5,8】和【2,3,6,7】,平均数为 4.5

思路:直接暴或者找公约数,但仔细思考一下会发现若能被t整除才能平分。

代码如下:

#include <iostream>
#include <stdio.h>
using namespace std;
const int maxn=105;
int a[maxn];
int gcd(int a,int b)
{
    while(a%b!=0)
    {
        int r=a%b;
        a=b;
        b=r;
    }
    return b;
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        cin>>n;
        int sum=0;
        for(int i=0;i<=n-1;i++) {cin>>a[i]; sum+=a[i];}
        if(sum==0) {cout<<"yes"<<endl; continue;}
        if(gcd(sum,n)==1) cout<<"no"<<endl;
        else cout<<"yes"<<endl;
    }
    return 0;
}

 

H.小q的数列

题目描述

小q最近迷上了各种好玩的数列,这天,他发现了一个有趣的数列,其递推公式如下:

f[0]=0 f[1]=1;
f[i]=f[i/2]+f[i%2];(i>=2)

现在,他想考考你,问:给你一个n,代表数列的第n项,你能不能马上说出f[n]的值是多少,以及f[n]所代表的值第一次出现在数列的哪一项中?(这里的意思是:可以发现这个数列里某几项的值是可能相等的,则存在这样一个关系f[n'] = f[n] = f[x/2]+f[x%2] = f[x]...(n'<n<x) 他们的值都相等,这里需要你输出最小的那个n'的值)(n<10^18)

输入描述:

输入第一行一个t
随后t行,每行一个数n,代表你需要求数列的第n项,和相应的n'
(t<4*10^5)

输出描述:

输出每行两个正整数
f[n]和n',以空格分隔

示例1

输入

2
0
1

输出

0 0
1 1

思路:第一反应是用递归,但肯定是会暴的,多列几项就会发现规律。挺水的

代码如下:

#include<iostream>
#include<algorithm>
#include<string>
#include<stdio.h>
#include<string.h>
#include<set>
#include<cmath>
using namespace std;
#define sf1(a) scanf("%d",&a)
#define sf2(a,b) scanf("%d%d",&a,&b)
#define fto(a,b) for(int t=a;t<b;t++)
int main()
{
    int T;
    scanf("%d", &T);    
    while (T--)
    {
        long long n;
        scanf("%lld", &n);
        int i, j;
        int cnt = 0;
        long long tmp = n;
        while (tmp>1)
        {
            if (tmp %2==1) cnt++;
            tmp /= 2;
        }
        cnt += tmp == 0 ? 0 : 1;
        long long ans=0;
        for (i = 0; i < cnt; i++)
        {
            ans = ans * 2 + 1;
        }
        printf("%d %lld\n", cnt,ans);
    }
    return 0;
}

 

I.小q的时钟

时间转换,水题,不谈。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值