第一届ACC(AcWing Cup)全国高校联赛(决赛)(C++和JAVA两种题解)

第一届ACC(AcWing Cup)全国高校联赛(决赛)

大赛简介:
ACC(AcWing Cup)高校联赛是由AcWing举办的算法比赛,难度与蓝桥杯省赛持平,比赛设立高校团体排行榜和丰厚奖金。
2021年,北京大学AcWing杯程序设计竞赛圆满举行;2022年,AcWing将与更多高校一起培养更多算法人才。
赛制:
ACM赛制,共90分钟,3道题目,每道题目记1分。
每道题目的罚时为AC时刻与比赛开始时刻所经过的时间。
通过题目的非AC提交每次罚时5分钟。
通过题目数相同时,总罚时短的同学排名靠前。

两个闹钟

有两个闹钟。

第一个闹钟会在 b,b+a,b+2a,b+3a,… 时刻响铃。

第二个闹钟会在 d,d+c,d+2c,d+3c,… 时刻响铃。

请计算两个闹钟第一次同时响铃的具体时刻。

输入格式
第一行包含两个整数 a,b。

第二行包含两个整数 c,d。

输出格式
一个整数,表示第一次同时响铃的具体时刻。

如果永远都不可能同时响铃,则输出 −1。

数据范围
所有测试点满足 1≤a,b,c,d≤100。

输入样例1:
20 2
9 19
输出样例1:
82
输入样例2:
2 1
16 12
输出样例2:
-1

第一题做的时候就没想什么,就直接暴力, 没想到直接过了

#include <bits/stdc++.h>

using namespace std;

int a, b, c, d;

int main() 
{
    
    cin >> a >> b >> c >> d;
    
    int res = -1;
    for(int i = 0; i < 1000 && res == -1; i++) 
    {
        for(int j = 0; j < 1000; j++) 
        {
            if(b - d == i * c - j * a) 
            {
                res = d + c * i;
                break;
            }
        }
    }
    
    cout << res << endl;
    
    return 0;
}

这里再附上JAVA代码:

import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        long a=sc.nextInt();
        long b=sc.nextInt();
        long c=sc.nextInt();
        long d=sc.nextInt();//输入四个数
        long arr[]=new long[103];
        long arr2[]=new long[103];//开两个较大的数组
        int n=102;
        for(int i=1;i<=n;i++){
            arr[i]=b;
            arr2[i]=d;
            b+=a;
            d+=c;//将b+a*i和d+c*i分别放进两个数组中
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(arr[i]==arr2[j]){
                    System.out.println(arr[i]);//遍历两个数组,如果相等就输出。
                    return;
                }
            }
        }
        System.out.println(-1);
    }
}

合并石子

小 A 面前有 n 堆石子排成一排,每堆石子的数量从左到右依次为 a1,a2,…,an。

小 B 面前有 m 堆石子排成一排,每堆石子的数量从左到右依次为 b1,b2,…,bm。

两人面前的石子总数相同,即 a1+a2+…+an=b1+b2+…+bm。

每个人都可以对自己面前的石子堆进行任意次合并操作,两个人的操作次数可以不同。

合并操作指将连续的若干堆相邻石子合并为一堆。

请你确定一个最大的整数 k,满足:

小 A 停止所有操作后,面前恰好有 k 堆石子,每堆石子的数量从左到右依次为 a′1,a′2,…,a′k。
小 B 停止所有操作后,面前恰好有 k 堆石子,每堆石子的数量从左到右依次为 b′1,b′2,…,b′k。
对于 1≤i≤k,a′i=b′i 均成立。
输出这个 k 的最大可能值。

输入格式
第一行包含两个整数 n,m。

第二行包含 n 个整数 a1,a2,…,an。

第三行包含 m 个整数 b1,b2,…,bm。

输出格式
一个整数,表示 k 的最大可能值。

数据范围
前三个测试点满足 1≤n,m≤10。
所有测试点满足 1≤n,m≤105,1≤ai,bi≤106,a1+a2+…+an=b1+b2+…+bm≤106。

输入样例1:
7 6
2 5 3 1 11 4 4
7 8 2 4 1 8
输出样例1:
3
输入样例2:
3 3
1 10 100
1 100 10
输出样例2:
2
输入样例3:
1 4
4
1 1 1 1
输出样例3:
1

这道题最开始没读懂题,也就没有思路,后来静下来又慢慢地一个一个字的读题,才发现应该是前缀和和双指针,然后就试了一下。

#include<bits/stdc++.h>

using namespace std;

const int N = 1e6 + 10;

int a[N],b[N],s1[N],s2[N];
int n , m;

int main()
{
    cin >> n >> m;
    for(int i = 1 ; i <= n ; i ++)
    {
        cin >> a[i]; s1[i] = s1[i-1] + a[i];
    }
    for(int j = 1 ; j <= m ; j ++) 
    {
        cin >> b[j]; s2[j] = s2[j-1] + b[j];
    }
    int res = 0,i = 1 , j = 1, k = 1 , l = 1;
    while(i <= n && j <= m)
    {
        if(s1[i] - s1[j-1] == s2[k] - s2[l-1])
        {
            res ++;
            i ++ , k ++ ;
            j = i;
            l = k;
        }
        else
        {
            if(s1[i] - s1[j-1] < s2[k] - s2[l-1]) i++;
            else k++;
        }
    }
    cout << res ;
    return 0;
}

也附上JAVA代码

import java.util.Scanner;

public class Main{
    public static void main(String[] args){
        int res = 0;
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        int[]a = new int[n + 1];
        int[]b = new int[m + 1];
        for(int i = 0; i < n; i++) a[i] = sc.nextInt();
        for(int i = 0; i < m; i++) b[i] = sc.nextInt();

        int i = 0;
        int j = 0;
        int c1 = a[0];
        int c2 = b[0];
        while(i < n || j < m){
            if(c1 == c2){
                res++;
                i++;
                j++;
                c1 = a[i];
                c2 = b[j];
            //如果c1小于c2就把c1后面加进来    
            }else if(c1 < c2){
                i++;
                c1 += a[i];
            //同理就行
            }else{
                j++;
                c2 += b[j];
            }
        }
        System.out.println(res);
    }
}

翻转树边

给定一个 n 个节点的树。

节点编号为 1∼n。

树中的 n−1 条边均为单向边。

现在,我们需要选取一个节点作为中心点,并希望从中心点出发可以到达其他所有节点。

但是,由于树中的边均为单向边,所以在选定中心点后,可能无法从中心点出发到达其他所有节点。

为此,我们需要翻转一些边的方向,从而使得所选中心点可以到达其他所有节点。

我们希望选定中心点后,所需翻转方向的边的数量尽可能少。

请你确定哪些点可以选定为中心点,并输出所需的最少翻转边数量。

输入格式
第一行包含整数 n。

接下来 n−1 行,每行包含两个整数 a,b,表示存在一条从 a 到 b 的单向边。

输出格式
第一行输出一个整数,表示所需的最少翻转边数量。

第二行以升序顺序输出所有可选中心点(即所需翻转边数量最少的中心点)的编号。

数据范围
前三个测试点满足 2≤n≤5。
所有测试点满足 2≤n≤2×105,1≤a,b≤n,a≠b。

输入样例1:
3
2 1
2 3
输出样例1:
0
2
输入样例2:
4
1 4
2 4
3 4
输出样例2:
2
1 2 3

第三题我是真的没读懂题,然后看了y总的直播到现在也没明白,先附上y总的代码吧。

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 200010, M = N * 2;

int n;
int h[N], e[M], w[M], ne[M], idx;
int down[N], up[N];

void add(int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}

void dfs_down(int u, int from)
{
    for (int i = h[u]; ~i; i = ne[i])
    {
        if (i == (from ^ 1)) continue;
        int j = e[i];
        dfs_down(j, i);
        down[u] += down[j] + w[i];
    }
}

void dfs_up(int u, int from)
{
    if (from != -1)
    {
        int fa = e[from ^ 1];
        up[u] = up[fa] + down[fa] - down[u] - w[from]  + w[from ^ 1];
    }

    for (int i = h[u]; ~i; i = ne[i])
    {
        if (i == (from ^ 1)) continue;
        int j = e[i];
        dfs_up(j, i);
    }
}

int main()
{
    scanf("%d", &n);
    memset(h, -1, sizeof h);
    for (int i = 0; i < n - 1; i ++ )
    {
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b, 0);
        add(b, a, 1);
    }

    dfs_down(1, -1);
    dfs_up(1, -1);

    int res = N;
    for (int i = 1; i <= n; i ++ )
        res = min(res, down[i] + up[i]);

    printf("%d\n", res);
    for (int i = 1; i <= n; i ++ )
        if (down[i] + up[i] == res)
            printf("%d ", i);

    return 0;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/3032110/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

小羊还是差得远呢,小羊加油吧。

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小羊努力变强

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值