A-牛牛玩平板

题目描述

https://ac.nowcoder.com/acm/contest/93583/A

思路

仔细读下题目,直接猜排序后就是最佳方案,一写,诶,还真是。

咳咳,不玩玄学,下面才是题解。

题目给出了两种操作,让我们选一个顺序使最后的结果最大话。既然这样,那就应该分析一下这两种操作最后贡献的价值。在正整数的情况下,普遍是乘法的贡献比加法高,那么是否可以按照类似于田忌赛马的策略进行安排呢?这个例子不行啊,乘法运算的结果可是爆发式的增长。

那么这样,就可以直接让小的数主要贡献加法,一直相加,使最后相乘时的结果最大化。换句话说就是所有小的数都放在前面处理,大的数全部放在后面处理。

代码

#include <stdio.h>

// const int N = 100 + 10;  牛客编译器又抽风了...
#define N 110
int a[N];

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
    }

    // 冒泡排序
    for (int i = 0; i < n; i++) {
        for (int j = i + 1; j < n; j++) {
            if (a[i] > a[j]) {
                int tmp = a[i];
                a[i] = a[j];
                a[j] = tmp;
            }
        }
    }

    // 按照题目的要求从小到大计算结果
    int ans = 0;
    for (int i = 1; i < n; i++) {
        ans += a[i - 1] * a[i];
        a[i] += a[i - 1];
    }
    printf("%d\n", ans);
    return 0;
}
n = int(input())
a = list(map(int, input().split()))
a.sort()
ans = 0
for i in range(1, n):
    ans += a[i] * a[i - 1]
    a[i] = a[i] + a[i - 1]
print(ans)

B-[NOIP2015]金币

题目描述

https://ac.nowcoder.com/acm/contest/93583/B

思路

额,题目要你一起跟着他的思路进行模拟,好像是恶心人的傻B题。

那就直接按照他的方式来吧,首先需要一个变量来记录今天需要发多少枚金币,然后还需要一个变量来统计这种发金币的方法已经用过几次了,如果发完了(也就是这两个变量相等的时候)就发下一种。别忘了清空统计次数的变量哦。

代码

#include 

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

    int ans = 0;
    int coin = 1;    // 统计现在该发几枚金币
    int cnt = 0;     // 统计这种方法用了几次
    for (int i = 1; i <= n; i++) {
        ans += coin;
        cnt += 1;
        // 如果发完了的话 就该发下一种了
        if (coin == cnt) {
            coin += 1;
            cnt = 0;
        }
    }
    printf("%d\n", ans);
    return 0;
}
n = int(input())
ans = 0
coin = 1  # 统计该发几枚金币
cnt = 0  # 统计这种硬币已经发了几次
for i in range(1, n + 1):
    ans += coin
    cnt += 1
    # 如果发完了的话 就该发下一种了
    if cnt == coin:
        coin += 1
        cnt = 0
print(ans)

C-等式

题目描述

https://ac.nowcoder.com/acm/contest/93583/C

思路

不会写,去找我们这的大佬问,结果大佬给出了两页纸的解题过程……听完后还是蒙。

代码


D-脸盆大哥的木桶

题目描述

https://ac.nowcoder.com/acm/contest/93583/D

思路

木桶效应,一个桶能够承载的容量取决于最短的那块木板。

既然这样,同时又需要最大化容量,那么就应该尽量拼出短板最长的桶,如果木板有多就应该直接抛弃最短的那部分。直接从大到小枚举木板长度即可。

代码

#include <stdio.h>

#define N 1010
int a[N];

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        int n, k, s;
        scanf("%d%d%d", &n, &k, &s);
        for (int i = 1; i <= n; i++) scanf("%d", &a[i]);

        for (int i = 1; i <= n; i++) {
            for (int j = i + 1; j <= n; j++) {
                if (a[i] < a[j]) {
                    int tmp = a[i];
                    a[i] = a[j];
                    a[j] = tmp;
                }
            }
        }

        int cnt = 0;
        int ans = 0;
        int minn = 1e9;
        for (int i = 1; i <= n; i++) {
            if (minn > a[i]) minn = a[i];
            cnt++;
            if (cnt == k) {
                ans += s * minn;
                cnt = 0;
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}
t = int(input())
while t:
    t -= 1
    n, k, s = map(int, input().split())
    a = list(map(int, input().split()))

    a.sort(reverse=True)

    ans = 0
    minn = int(1e9)
    cnt = 0
    for i in range(n):
        minn = min(minn, a[i])
        cnt += 1
        if cnt == k:
            cnt = 0
            ans += s * minn
    print(ans)

E-Mountain

题目描述

https://ac.nowcoder.com/acm/contest/93583/E

思路

根据题意画出图形:

因为不会用电脑画图,所以直接用Excel生成了一个图,不要在意这些细节

其中绿色表示滑索,红色表示海拔的变化。红色部分的总和就是 $1+2+2+1+2+2=10$。

你应该可以发现什么吧,没有发现?再看下一个例子:

因为题目中也说明了可以不通过中间海拔比较低的山,那么$4$和$5$也就可以直接跳过了,最后红色部分的总和是 $1+2+2+4+1=10$。

咳咳,剩下的部分就不需要我说明了吧……

还是没看出来?再仔细分析一下我们上面得到的图形,将得到的红色部分平移一下,让同一边的线全部拼在一起,那么就可以得到下面的图形:

那么也就是变成了求最左右这两边的红线的长度,红色部分的长度是多少应该很容易想得到了吧,也就是最高的那座山的高度(毕竟红线要完全覆盖嘛,总不能在半山腰打个洞钻过去吧)。

那么就可以得出,这道题的答案就是最高的那座山的高度乘以2。

代码

#include <stdio.h>

const int N = 1000 + 10;
int a[N];

int main() {
    int n;
    scanf("%d", &n);
    int maxn = 0;// 记录最大值
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
        if (maxn < a[i]) maxn = a[i];
    }
    printf("%d\n", maxn * 2);
    return 0;
}
n = int(input())
a = list(map(int, input().split()))
print(max(a) * 2)

F-最小生成树

题目描述

https://ac.nowcoder.com/acm/contest/93583/F

思路

最小生成树算法?这么经典?立马掏出板子(代码模板)来看一眼,不对劲,很不对劲,图上有 $10^5$ 个点,还是完全图,这光是连边就炸掉了啊。(大概有 $5 \times 10^9$条边)

完全图(这里可以点)

完全图指的是这样的一类图:图中每个点都有边与其他点相连,并且没有重边和自环。看图你就明白了:

图源:维基百科

在仔细看看,权值都是落于点上的,这意味着什么呢。不懂,画个图看看

假设1,2,3,4号点的权值对应的就是1,2,3,4那么要怎样才能使最后得到的边权最小呢,题目中说一条边 $(u,v)$ 的权值就是 $a_u + a_v$ ,那么最小的方法应该就是使最后的 $ans = (a_{u_1}+a_{v_1}) + \dots + (a_{u_{n-1}}+a_{v_{n-1}})$ 最小。因为最后生成的图是“最小生成树”,也就是说图上所有点都是相连的,那么每个点都至少会被加一次,如果把这个必须会被加上的数看成终点的话,那么我们的目的也就是变成了要如何求权值最小的起点。

是不是有点头绪了,那么这个最小的起点该怎么找呢,因为题目中给出的是完全图,也就是说图中任取一个点,这个点都可以抵达图中其他任意一点,直接把权值最小的那个点设置为起点,那么最后得到的边权也就是最小的。上图的最终结果也就是 $ans = (a_{min} + a_{v_1}) + \dots + (a_{min} + a{v_n}) - 2 \times a_{min}$ ,最后为什么要减去 $2 \times a_{min}$ 呢,因为最后得到的图只有 $n - 1$ 条边,但是计算了 $n$ 条边的情况,需要从中减去 $a_{min}$ 以自己为起点同时以自己为终点的情况。

代码

#include <stdio.h>

#define N 100010
long long a[N];

int main() {
    int n;
    scanf("%d", &n);
    int minn = 1e9;// 记录出现的最小值
    for (int i = 1; i <= n; i++) {
        scanf("%lld", &a[i]);
        if (minn > a[i]) minn = a[i];
    }

    long long ans = 0;
    for (int i = 1; i <= n; i++) {
        ans += minn + a[i];// 把每个点都作为终点加一遍
    }
    printf("%lld\n", ans - 2 * minn);
    return 0;
}
n = int(input())
a = list(map(int, input().split()))
minn = min(a)
ans = 0
for i in a:
    ans += i + minn
print(ans - minn * 2)

写在最后

这些题目都还是有点难度的,主要还是面向已经学完了编程语言的基础语法的同学,没有写出来也不要灰心,毕竟还是有点难度的(比如这个C题)。因为看到群里很多人都会python,所以提供了python的代码,以后可能会统一使用 C++作为题解的标准语言(当然,可能也会有一些python的奇技淫巧),不过编程语言这方面并不用当心,语言只是解题的工具,正真重要的还是自己解题的思维。

写题解的时候发现了一些bug,有的时候数学公式可能并没有正确的加载,如果看见了奇怪的$符号刷新一下页面就可以了。

例子:

$$
\nabla \cdot \mathbf{B} = 0
$$
$$
\oint_{\partial V} \mathbf{B} \cdot d\mathbf{A} = 0
$$