2020 BNUZ 新生网络赛 题解

131 篇文章 0 订阅

校运会新项目

先按照时间排序,相同时间排名相同,按照排名从高到低排序,相同排名按照字典序排序

具体可以看代码是如何实现的

#include <stdio.h>
#include <string.h>

struct Student {
    char name[21];
    int m, s;
} stu[10], swap;

int main() {
    int t;
    scanf("%d", &t);
    for (int i = 1; i <= t; i++) {
        int n;
        scanf("%d", &n);
        for (int j = 0; j < n; j++) {
            scanf("%s %d:%d", stu[j].name, &stu[j].m, &stu[j].s);
        }
        for (int j = 0; j < n - 1; j++) {//冒泡排序
            for (int k = 0; k < n - 1 - j; k++) {
                if ((stu[k].m > stu[k + 1].m) || (stu[k].m == stu[k + 1].m && stu[k].s > stu[k + 1].s) ||
                    (stu[k].m == stu[k + 1].m && stu[k].s == stu[k + 1].s &&
                     strcmp(stu[k].name, stu[k + 1].name) > 0)) {
                    swap = stu[k];
                    stu[k] = stu[k + 1];
                    stu[k + 1] = swap;
                }
            }
        }
        int u = 1;
        printf("Case #%d:\n", i);
        printf("%s %d\n", stu[0].name, u);
        for (int j = 1; j < n; j++) {
            if (stu[j].m != stu[j - 1].m || stu[j].s != stu[j - 1].s)//排名
                u++;
            printf("%s %d\n", stu[j].name, u);
        }
    }
    return 0;
}

小蚂蚁找位置

依据题意返回数组下标即可,代码实现可以看下面

#include<stdio.h>

int searchInsert(int *a, int n, int target) {
    for (int i = 0; i < n; i++) {
        if (a[i] >= target)
            return i;
    }
    return n;
}

int main() {
    int T;
    while (~scanf("%d", &T)) {
        int a[2000], n, target;
        for (int T1 = 1; T1 <= T; T1++) {
            scanf("%d", &n);
            for (int i = 0; i < n; i++) {
                scanf("%d", &a[i]);
            }
            scanf("%d", &target);
            printf("Case #%d:\n%d\n", T1, searchInsert(a, n, target));
        }
    }
}

简单的飞行棋

如果某一个作为起跳点的格子可以跳跃的距离是4,那么表示后面4个格子都可以作为起跳点。
可以对每一个能作为起跳点的格子都尝试跳一次,把能跳到最远的距离不断更新。
如果可以一直跳到最后,就成功了。

#include<stdio.h>

int max(int a, int b) {
    if (a > b)
        b = a;
    return b;
}

int main() {
    int n, a[1000];
    int m;
    while (~scanf("%d", &m)) {
        int ans = 1;
        while (m--) {
            int m1 = 0;
            int max1 = 0;
            scanf("%d", &n);
            for (int i = 0; i < n; i++) {
                scanf("%d", &a[i]);
            }
            printf("Case #%d:\n", ans++);
            for (int i = 0; i < n; i++) {
                if (i <= max1) {
                    max1 = max(max1, i + a[i]);//i指位置,a[i]指可以跳到的位置
                    if (i >= n - 1 && m1 != 1) {
                        printf("true\n");
                        m1 = 1;
                        break;
                    }
                }
            }
            if (m1 == 0) {
                printf("false\n");
            }
        }
    }
}

宿舍内战

由规律可知每次当数字为偶数的时候lmm必胜

可以自行多举例子,由规律可知每次当数字为偶数的时候lmm必胜
M=1 的时候,区间 (0, 1) 中没有整数是 n 的因数,所以此时 LMM 败。
M= 2 的时候,LMM 只能拿 1,M 变成 1,ZMM 无法继续操作,故 LMM 胜。
M = 3 的时候,LMM 只能拿 1,M 变成 2,根据 M=2 的结论,我们知道此时 ZMM 会获胜,LMM 败。
M = 4 的时候,LMM 能拿 1 或 2,如果 LMM 拿 1,根据 M=3 的结论,ZMM 会失败,LMM 会获胜。
M = 5 的时候,LMM 只能拿 1,根据 M=4 的结论,LMM 会失败。

#include<stdio.h>

int main() {
    int m, n, t;
    while (~scanf("%d", &t)) {

        int ans = 1;
        while (t--) {
            scanf("%d", &m);
            printf("Case #%d:\n", ans++);
            if (m % 2 == 0)
                printf("true\n");
            else
                printf("false\n");

        }
    }
} 

后海有树的院子

暴力时间会超限

而且,最后的结果超出了int的范围,需要使用long long

需要使用前缀和的预处理方式

一维前缀和代码

#include<stdio.h>
#include<string.h>

#define ll long long
ll cb[2001][2001];

int main() {
    ll t;
    memset(cb, 0, sizeof(cb));
    scanf("%lld", &t);
    ll l, w;
    scanf("%lld %lld", &l, &w);
    ll kk;
    for (ll i = 1; i <= l; i++)
        for (ll j = 1; j <= w; j++) {
            scanf("%lld", &kk);
            cb[i][j] = kk + cb[i][j - 1];
        }
    for (int i = 1; i <= t; i++) {
        ll n, m;
        ll ans = 0;
        scanf("%lld %lld", &n, &m);
        for (ll j = 1; j <= n; j++) {
            ans += cb[j][m];
        }
        printf("Case #%lld:\n%lld\n", i, ans);
    }
}

买比特币的最佳时机

暴力也能过,没卡时间

暴力代码:

#include<stdio.h>

int main() {
    int t, s = 1;
    scanf("%d", &t);
    while (t--) {
        int i, j;
        printf("Case #%d:\n", s++);
        int all[40];
        int n;
        int num = 0;
        scanf("%d", &n);
        for (i = 0; i < n; i++) {
            scanf("%d", &all[i]);
        }
        for (i = 0; i < n; i++) {
            for (j = i; j < n; j++) {
                int k = all[j] - all[i];
                if (k > num) {
                    num = k;
                }
            }
        }
        printf("%d\n", num);
    }
}

可以看看下面dp的写法
dp四部曲:
1.确定状态 :状态就是前i天的最大收益;
2.转移方程 :前i天的最大收益 = max{前i-1天的最大收益,第i天的价格-前i-1天中的最小价格};
3.初始条件和边界条件 :把min设置成第一天的价格,让利润b=0;
4.计算顺序 :从头到尾,从第二天(数组下标为1)开始计算。

#include<stdio.h>

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

int main() {
    int n, T;
    while (~scanf("%d", &T)) {
        for (int T1 = 1; T1 <= T; T1++) {
            scanf("%d", &n);
            int prices[100];
            for (int i = 0; i < n; i++)
                scanf("%d", &prices[i]);
            int min = prices[0];
            int b = 0;
            for (int i = 1; i < n; i++) {
                if (prices[i] < min) {
                    min = prices[i];
                }
                if (i == 1) {
                    prices[i] = max(0, prices[i] - min);
                } else {
                    prices[i] = max(prices[i - 1], prices[i] - min);
                }
            }
            printf("Case #%d:\n%d\n", T1, prices[n - 1]);
        }
    }
    return 0;
} 

诗句里选取的意义

两个字母交换

注意的是,下标从1开始

而且给出的是启焦的操作,我们如果复原需要倒序进行操作

#include<stdio.h>

int main() {
    int t, a[101], b[101], k;
    char str[1190];
    scanf("%d", &t);
    for (int q = 1; q <= t; q++) {
        getchar();
        gets(str + 1);
        scanf("%d", &k);
        for (int l = 1; l <= k; l++) {
            scanf("%d %d", &a[l], &b[l]);
        }
        for (int l = k; l >= 1; l--) {
            if (str[a[l]] != str[b[l]])
                str[a[l]] ^= str[b[l]] ^= str[a[l]] ^= str[b[l]];
        }
        printf("Case #%d:\n", q);
        puts(str + 1);
    }
}

逃出剑剑的魔爪

我们可以先考虑总共有多少道排列方式, 由于n个格子,每个位置有m种仓鼠物种,所以根据乘法原理,一共有m^n 种取法。
然后我们再考虑不会逃出盒子的情况,这样我们只要保证当前这一个与前面一个不同就可以了,所以不会逃出盒子的情况有m* (m-1)^(n-1)种取法。
所以很容易得到答案就是m^n - m* (m-1)^(n-1),但是普通的算法会爆时间,要用快速幂

#include<stdio.h>

#define MOD 100003
#define ll long long

ll mod_pow(ll x, ll n) //求x的n次方
{
    ll ans = 1;
    while (n) {
        if (n & 1) ans = (ans * x) % MOD; //判断n是否为奇数,奇数乘上x
        x = (x * x) % MOD;              //将x平方
        n >>= 1;                //将n除以2
    }
    return ans;
}

int main() {
    int t;
    while (~scanf("%d", &t)) {
        ll m, n, ans;
        int j = 0;
        for (int i = 0; i < t; i++) {
            scanf("%lld %lld", &m, &n);
            ans = (mod_pow(m, n) - (m * mod_pow(m - 1, n - 1)) % MOD) % MOD;
            if (ans < 0) ans += MOD;
            j++;
            printf("Case #%d:\n%lld\n", j, ans);
        }
    }
    return 0;

}

010和101

注意:子序列的概念

题目大意

给你只有0和1组成的字符串,你每次操作可以将某一位上的0翻转成1,1翻转成0。求最少反转多少次才能使得字符串中没有“101”或者“010”

分析

为了让字符串中不包括子序列“010”或“101”,那么我们修改后的字符串只有3种可能:全是0,全是1,一边是0一边是1(如0001111)这三种情况

一边是0一边是1的情况会有一个分割线,例如000|111这里就是有一个0和1的分割线,如果全为0或者1,就是分割线在最左边或者最右边的情况

因此,我们可以枚举这个分割线的所有位置,并计算出分割线在该位置时需要的最小修改次数,最后找出最小的修改次数即可。

#include <stdio.h>
#include <string.h>

int min(int a, int b);

int main() {
    int t;
    scanf("%d", &t);
    for (int i = 1; i <= t; ++i) {
        char s[1000];
        scanf("%s", s);
        int cnt0 = 0, cnt1 = 0;
        //表示分割线的右边的0/1数量
        //因为一开始分割线位于字符串的最左边(|00010010)
        for (int j = 0; j < (strlen(s)); j++) {
            //所以一开始cnt存的是整个字符串中0/1的数量
            if (s[j] == '0') cnt0++;
            else cnt1++;
        }
        int k0 = 0, k1 = 0;
        //表示分割线左边0/1的数量(一开始都是0)
        int ans = min(cnt0, cnt1);
        //记录答案(全局最小修改次数)
        for (int j = 0; j < strlen(s); j++)
        {
            //枚举分割线的位置
            if (s[j] == '0'){
                k0++, cnt0--;
            }
            //分割线右移,cnt和k相应的增减
            else {
                k1++, cnt1--;
            }
            
            ans = min(ans, min(k0, k1) + min(cnt0, cnt1));
            //更新最小值
        }
        printf("Case #%d:\n%d\n", i, ans);
    }
    return 0;
}

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

乌鸦和写字台

题意:

给出凸多边形的每一个角的坐标,求出任意三点围成的三角形面积的最小值

思路:

我们可以确定,相邻的三个点组成的三角形才有可能是最短的,然后遍历一遍就行

#include<stdio.h>
#include<math.h>

double x[10000];
double y[10000];
double area[10000];

double getArea(double x1, double y1, double x2, double y2, double x3, double y3) {//求面积
    double a, b, c;
    a = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
    b = sqrt((x1 - x3) * (x1 - x3) + (y1 - y3) * (y1 - y3));
    c = sqrt((x2 - x3) * (x2 - x3) + (y2 - y3) * (y2 - y3));
    double p = (a + b + c) / 2;
    double ans = sqrt(p * (p - a) * (p - b) * (p - c));
    return ans;
}

int main() {
    int t;
    scanf("%d", &t);
    for (int q = 1; q <= t; q++) {
        int n;
        scanf("%d", &n);
        for (int j = 1; j <= n; j++) {
            scanf("%lf %lf", &x[j], &y[j]);
        }

        x[0] = x[n], y[0] = y[n];
        for (int i = 1; i < n; i++) {//统计每个三角形的面积
            area[i] = getArea(x[i], y[i], x[i - 1], y[i - 1], x[i + 1], y[i + 1]);
        }
        area[n] = getArea(x[n], y[n], x[n - 1], y[n - 1], x[1], y[1]);

        double min = 1e5;
        for (int i = 1; i <= n; i++) {//求最小
            if (min > area[i]) {
                min = area[i];
            }
        }
        printf("Case #%d:\n%.2lf\n", q, min);
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值