第八届传智杯省赛第一场题解

第八届传智杯省赛第一场题解

第一题:

**【回忆题目】**已知3 张一星卡可以合成 1 张二星卡。现在给定你拥有的一星卡数量 n,请计算 最多可以合成多少张二星卡。

代码实现

C语言

#include <stdio.h>

int main() {
    int n;
    scanf("%d", &n);

    int ans = n / 3;
    printf("%d\n", ans);
    return 0;
}

Python

def solve():
    n = int(input())
    
    ans = n // 3
    print(ans)


if __name__ == "__main__":
    solve()

第二题:

**【回忆题目】**Alice 和 Bob 正在进行一场打牌游戏。Alice 初始拥有 (n) 张牌,Bob 初始拥有 (m) 张牌

游戏规则:

  1. 每回合,Alice 和 Bob 各从自己牌堆最上方出一张牌;

  2. 点数大的玩家收回自己出的牌,并按出牌顺序放到自己牌堆最下方;

  3. 若点数相等,两张牌丢弃;

  4. 当任一玩家没有牌时,游戏结束,还有牌的玩家获胜。

  • Alice 获胜 → 输出 Alice,Bob 获胜 → 输出 Bob,双方同时没牌 → 输出 Draw

代码实现

C语言

#include <stdio.h>

int main() {
    int alice[2000], bob[2000];
    int n, m;
    scanf("%d", &n);
    scanf("%d", &m);
    for(int i=0;i<n;i++) scanf("%d", &alice[i]);
    for(int i=0;i<m;i++) scanf("%d", &bob[i]);

    int af=0, ar=n; // Alice front/rear
    int bf=0, br=m; // Bob front/rear

    while(af < ar && bf < br){
        int a = alice[af++];
        int b = bob[bf++];

        if(a > b){
            alice[ar++] = a; // 只收回自己的牌
        } else if(b > a){
            bob[br++] = b; // 只收回自己的牌
        }
        // a == b 时,丢弃
    }

    if(af < ar && bf >= br) printf("Alice\n");
    else if(bf < br && af >= ar) printf("Bob\n");
    else printf("Draw\n");

    return 0;
}

Python

def play_game(alice_cards, bob_cards):
    n, m = len(alice_cards), len(bob_cards)
    # front 和 rear 指针
    alice_front, alice_rear = 0, n
    bob_front, bob_rear = 0, m

    while alice_front < alice_rear and bob_front < bob_rear:
        a = alice_cards[alice_front]
        b = bob_cards[bob_front]
        alice_front += 1
        bob_front += 1

        if a > b:
            alice_cards.append(a)
            alice_rear += 1
        elif b > a:
            bob_cards.append(b)
            bob_rear += 1
        # a == b 时,丢弃两张牌

    if alice_front < alice_rear and bob_front >= bob_rear:
        return "Alice"
    elif bob_front < bob_rear and alice_front >= alice_rear:
        return "Bob"
    else:
        return "Draw"

if __name__ == "__main__":
    n, m = map(int, input().split())
    alice_cards = list(map(int, input().split()))
    bob_cards = list(map(int, input().split()))
    print(play_game(alice_cards, bob_cards))

第三题:

【回忆题目】ttt组数据,原数组有$ n $个整数,其中一个数被墨水盖住(未知)。

现在输入 $n-1 $个数(数组中剩下的数),要求找出原数组里可能形成的最长连续序列长度,其中连续序列的定义为:
nums[i]+1=nums[i+1] nums[i] + 1 = nums[i+1] nums[i]+1=nums[i+1]

代码实现

C语言

#include <stdio.h>

int max(int a, int b) {
    return a > b ? a : b;
}

int main() {
    int t;
    scanf("%d", &t);

    while(t--) {
        int n;
        scanf("%d", &n);
        int nums[n-1];
        for(int i=0; i<n-1; i++) {
            scanf("%d", &nums[i]);
        }

        int last1 = -1, last2 = -1, lastnum1 = 0, lastnum2 = -1;
        int l = 0, r = 0, len = 1;
        int maxLen = 1, id = 0;

        while(r + 1 < n - 1) {
            id = 0;
            while(r + 1 < n - 1 && nums[r] + 1 == nums[r + 1]) {
                r++;
            }
            len = r - l + 1;

            if(last2 == l - 1 && (lastnum2 == -1 || lastnum2 == nums[l] - 2)) {
                len += lastnum2 - lastnum1 + 2;
                id = 1;
            }

            if(len > maxLen) maxLen = len;
            last1 = l; last2 = r; lastnum1 = nums[l]; lastnum2 = nums[r];
            l = ++r;
        }

        if(id == 0) {
            if(len + 1 > maxLen) maxLen = len + 1;
        }

        printf("%d\n", maxLen);
    }

    return 0;
}

Python

def solve(nums, n):
    last1 = -1
    last2 = -1
    lastnum1 = 0
    lastnum2 = -1
    l = 0
    r = 0
    len_seq = 1
    maxLen = 1
    id_flag = 0

    while r + 1 < n - 1:
        id_flag = 0
        while r + 1 < n - 1 and nums[r] + 1 == nums[r + 1]:
            r += 1
        len_seq = r - l + 1

        if last2 == l - 1 and (lastnum2 == -1 or lastnum2 == nums[l] - 2):
            len_seq += lastnum2 - lastnum1 + 2
            id_flag = 1

        maxLen = max(maxLen, len_seq)
        last1, last2 = l, r
        lastnum1, lastnum2 = nums[l], nums[r]
        l = r = r + 1

    if id_flag == 0:
        maxLen = max(maxLen, len_seq + 1)

    return maxLen

if __name__ == "__main__":
    t = int(input())
    for _ in range(t):
        n = int(input())
        nums = list(map(int, input().split()))
        print(solve(nums, n))

第四题:

【回忆题目】给定一个长度为 (n) 的整数数组 nums,以及一个整数 (m)(子数组长度),求所有长度至少为 (m) 的连续子数组的中位数的最大值。

  • 子数组:数组中连续的一段元素
  • 中位数:对于长度为 (k) 的子数组,将其排序后,中间位置的元素(如果长度为偶数,取下中位数,即第 (k/2) 个元素,0 下标)

输入格式

  • 第一行:整数 (t),表示测试用例数
  • 每个测试用例
    • 第一行:两个整数 (n) 和 (m)
    • 第二行:(n) 个整数,表示数组 nums

输出格式

  • 对每个测试用例输出一个整数:所有长度 ≥ (m) 的子数组中位数的最大值\

思路:二分+滑动窗口

  1. 二分答案:假设中位数最大值为 x,我们可以二分搜索 x

  2. 转换数组

    • nums[i] 转换为 +1 / -1:

      nums[i] >= x → 1
      nums[i] < x → -1
      
    • 这样问题转化为:是否存在长度 ≥ m 的子数组,使得1 的数量 ≥ -1 的数量(等价于前缀和 ≥ 0)

  3. 前缀和 + 滑动最小值判断

    • 计算前缀和 pre[i]
    • 对每个 i,检查 pre[i] - min(pre[j]) >= 0,其中 j 在窗口长度限制下
    • 如果存在,则中位数可以 ≥ x

代码实现

C语言

#include <stdio.h>

int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}

int check(int nums[], int n, int m, int x){
    int pre[n+1];
    pre[0] = 0;
    for(int i=0;i<n;i++){
        pre[i+1] = pre[i] + (nums[i]>=x ? 1 : -1);
    }
    int min_pre = 0;
    for(int i=m;i<=n;i++){
        if(pre[i]-min_pre>=0) return 1;
        min_pre = min(min_pre, pre[i-m+1]);
    }
    return 0;
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m;
        scanf("%d%d",&n,&m);
        int nums[n];
        int mn=1e9,mx=-1e9;
        for(int i=0;i<n;i++){
            scanf("%d",&nums[i]);
            if(nums[i]<mn) mn=nums[i];
            if(nums[i]>mx) mx=nums[i];
        }
        int l=mn,r=mx,ans=mn;
        while(l<=r){
            int mid=(l+r)/2;
            if(check(nums,n,m,mid)){
                ans=mid;
                l=mid+1;
            }else{
                r=mid-1;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

Python

def check(nums, m, x):
    n = len(nums)
    a = [1 if num >= x else -1 for num in nums]
    pre = [0]*(n+1)
    for i in range(n):
        pre[i+1] = pre[i] + a[i]

    min_pre = 0
    for i in range(m, n+1):
        if pre[i] - min_pre >= 0:
            return True
        min_pre = min(min_pre, pre[i - m + 1])
    return False

def max_median(nums, m):
    l, r = min(nums), max(nums)
    ans = l
    while l <= r:
        mid = (l + r) // 2
        if check(nums, m, mid):
            ans = mid
            l = mid + 1
        else:
            r = mid - 1
    return ans

if __name__ == "__main__":
    t = int(input())
    for _ in range(t):
        n, m = map(int, input().split())
        nums = list(map(int, input().split()))
        print(max_median(nums, m))

第五题:

**【回忆题目】**已知两个正整数 (a) 和 (b)。定义函数:
f(x)=lcm(a+x,b+x)gcd⁡(a+x,b+x) f(x) = \frac{\text{lcm}(a+x, b+x)}{\gcd(a+x, b+x)} f(x)=gcd(a+x,b+x)lcm(a+x,b+x)
求一个非负整数 xxx,使得f(x)f(x)f(x)的值最小,并输出这个最小值

思路

已知两个正整数 (a) 和 (b),定义函数:
f(x)=lcm(a+x,b+x)gcd(a+x,b+x),x≥0 f(x) = \frac{\mathrm{lcm}(a+x, b+x)}{\mathrm{gcd}(a+x, b+x)}, \quad x \ge 0 f(x)=gcd(a+x,b+x)lcm(a+x,b+x),x0
利用$ lcm-gcd$ 关系式:
lcm(u,v)=u⋅vgcd(u,v) \mathrm{lcm}(u,v) = \frac{u \cdot v}{\mathrm{gcd}(u,v)} lcm(u,v)=gcd(u,v)uv
所以
f(x)=(a+x)(b+x)gcd(a+x,b+x)gcd(a+x,b+x)=(a+x)(b+x)gcd(a+x,b+x)2 f(x) = \frac{\frac{(a+x)(b+x)}{\mathrm{gcd}(a+x, b+x)}}{\mathrm{gcd}(a+x, b+x)} = \frac{(a+x)(b+x)}{\mathrm{gcd}(a+x, b+x)^2} f(x)=gcd(a+x,b+x)gcd(a+x,b+x)(a+x)(b+x)=gcd(a+x,b+x)2(a+x)(b+x)

d=b−a d = b - a d=ba

gcd(a+x,b+x)=gcd(a+x,a+d+x)=gcd(a+x,d) \mathrm{gcd}(a+x, b+x) = \mathrm{gcd}(a+x, a+d+x) = \mathrm{gcd}(a+x, d) gcd(a+x,b+x)=gcd(a+x,a+d+x)=gcd(a+x,d)
所以函数可写为:
f(x)=(a+x)(a+d+x)gcd(a+x,d)2 f(x) = \frac{(a+x)(a+d+x)}{\mathrm{gcd}(a+x, d)^2} f(x)=gcd(a+x,d)2(a+x)(a+d+x)

结论:最小化 f(xf(xf(x) 等价于最大化 gcd(a+x,d)\mathrm{gcd}(a+x, d)gcd(a+x,d)


g=gcd(a+x,d) g = \mathrm{gcd}(a+x, d) g=gcd(a+x,d)

  • 则 (g) 必须是 (d) 的约数:g∣dg \mid dgd
  • 同时$ x \ge 0$且满足:

a+x≡0(modg)  ⟹  x≡−a(modg) a+x \equiv 0 \pmod{g} \implies x \equiv -a \pmod{g} a+x0(modg)xa(modg)

对每个 g∣dg \mid dgd

求最小非负整数 xxx 使得
x≡−a(modg),x≥0 x \equiv -a \pmod{g}, \quad x \ge 0 xa(modg),x0
计算对应的 f(x)f(x)f(x)
f(x)=(a+x)(b+x)g2=(a+x)(a+d+x)g2 f(x) = \frac{(a+x)(b+x)}{g^2} = \frac{(a+x)(a+d+x)}{g^2} f(x)=g2(a+x)(b+x)=g2(a+x)(a+d+x)
最小值为所有约数的最小值:
fmin⁡=min⁡g∣d(a+x)(a+d+x)g2 f_{\min} = \min_{g \mid d} \frac{(a+x)(a+d+x)}{g^2} fmin=gdming2(a+x)(a+d+x)

  • 如果 (a = b),则 (d = 0),此时:

f(0)=a2a2=1 f(0) = \frac{a^2}{a^2} = 1 f(0)=a2a2=1

  • 所以最小值为 (1)。

最终公式:
fmin⁡=min⁡g∣d(a+x)(b+x)g2,x=(g−a mod g) mod g f_{\min} = \min_{g \mid d} \frac{(a+x)(b+x)}{g^2}, \quad x = (g - a \bmod g) \bmod g fmin=gdming2(a+x)(b+x),x=(gamodg)modg

代码实现

C语言

#include <stdio.h>
#include <stdlib.h>

long long gcd(long long a, long long b){
    while(b){ long long t=b; b=a%b; a=t;}
    return a;
}

int main(){
    long long a,b;
    scanf("%lld%lld",&a,&b);
    long long d = llabs(b-a);

    if(d==0){
        printf("1\n");
        return 0;
    }

    long long min_val = (long long)1e18;
    for(long long i=1;i*i<=d;i++){
        if(d%i==0){
            long long k1 = i;
            long long k2 = d/i;
            long long ks[2] = {k1, k2};
            for(int j=0;j<2;j++){
                long long k = ks[j];
                long long x = (k - a%k)%k;
                long long g = gcd(a+x, b+x);
                long long val = (a+x)*(b+x)/(g*g);
                if(val < min_val) min_val = val;
            }
        }
    }
    printf("%lld\n", min_val);
    return 0;
}

Python

import math

def get_divisors(n):
    divs = set()
    for i in range(1, int(n**0.5)+1):
        if n % i == 0:
            divs.add(i)
            divs.add(n//i)
    return divs

def min_lcm_gcd(a, b):
    d = abs(b - a)
    if d == 0:
        return 1  # a == b, f(0) = 1

    min_val = float('inf')
    for k in get_divisors(d):
        # x >= 0, make (a+x) divisible by k
        x = (k - a % k) % k
        g = math.gcd(a+x, b+x)
        val = (a+x)*(b+x)//(g*g)
        min_val = min(min_val, val)
    return min_val

if __name__ == "__main__":
    a, b = map(int, input().split())
    print(min_lcm_gcd(a, b))

第六题

更新中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值