第八届传智杯省赛第一场题解
第一题:
**【回忆题目】**已知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) 张牌
游戏规则:
-
每回合,Alice 和 Bob 各从自己牌堆最上方出一张牌;
-
点数大的玩家收回自己出的牌,并按出牌顺序放到自己牌堆最下方;
-
若点数相等,两张牌丢弃;
-
当任一玩家没有牌时,游戏结束,还有牌的玩家获胜。
- 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) 的子数组中位数的最大值\
思路:二分+滑动窗口
-
二分答案:假设中位数最大值为
x,我们可以二分搜索x -
转换数组:
-
将
nums[i]转换为 +1 / -1:nums[i] >= x → 1 nums[i] < x → -1 -
这样问题转化为:是否存在长度 ≥ m 的子数组,使得1 的数量 ≥ -1 的数量(等价于前缀和 ≥ 0)
-
-
前缀和 + 滑动最小值判断:
- 计算前缀和
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),x≥0
利用$ 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)u⋅v
所以
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=b−a
则
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 dg∣d
- 同时$ x \ge 0$且满足:
a+x≡0(modg) ⟹ x≡−a(modg) a+x \equiv 0 \pmod{g} \implies x \equiv -a \pmod{g} a+x≡0(modg)⟹x≡−a(modg)
对每个 g∣dg \mid dg∣d:
求最小非负整数 xxx 使得
x≡−a(modg),x≥0
x \equiv -a \pmod{g}, \quad x \ge 0
x≡−a(modg),x≥0
计算对应的 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=ming∣d(a+x)(a+d+x)g2
f_{\min} = \min_{g \mid d} \frac{(a+x)(a+d+x)}{g^2}
fmin=g∣dming2(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=ming∣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=g∣dming2(a+x)(b+x),x=(g−amodg)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))
第六题
更新中

被折叠的 条评论
为什么被折叠?



