C语言之产值调整(蓝桥杯省B)

题目描述

偏远的小镇上,三兄弟共同经营着一家小型矿业公司"兄弟矿业"。公司旗下有三座矿山:金矿、银矿和铜矿,它们的初始产值分别用非负整数 A、B 和 C 表示。这些矿山的产出是小镇经济的核心,支撑着三兄弟和许多矿工家庭的生计。

然而,各矿山的产值波动剧烈,有时金矿收益高而银矿、铜矿低迷,有时则相反。这种不稳定性让公司收入难以预测,也常引发兄弟间的争执。为了稳定经营,三兄弟设计了一个公平的产值调整策略,每年执行一次,每次调整时,将根据当前的产值 A、B、C,计算新产值:

  1. 金矿新产值:A′=⌊(B+C)/2⌋;
  2. 银矿新产值:B′=⌊(A+C)/2⌋;
  3. 铜矿新产值:C′=⌊(A+B)/2⌋;

其中,⌊⌋ 表示向下取整。例如,⌊3.7⌋=3,⌊5.2⌋=5。

计算出 A′、B′、C′ 后,同时更新:A 变为 A′,B 变为 B′,C 变为 C′,作为下一年调整的基础。

三兄弟认为这个方法能平衡产值波动,于是计划连续执行 K 次调整。现在,请你帮他们计算,经过 K 次调整后,金矿、银矿和铜矿的产值分别是多少。

输入格式

输入的第一行包含一个整数 T,表示测试用例的数量。

接下来的 T 行,每行包含四个整数 A,B,C,K,分别表示金矿、银矿和铜矿的初始产值,以及需要执行的调整次数。

输出格式

对于每个测试用例,输出一行,包含三个整数,表示经过 K 次调整后金矿、银矿和铜矿的产值,用空格分隔。

cs 复制代码
输入
2
10 20 30 1
5 5 5 3
输出
25 20 15
5 5 5

说明/提示

评测用例规模与约定

  • 对于 30% 的评测用例,1≤T≤100,1≤A,B,C,K≤10^5。
  • 对于 100% 的评测用例,1≤T≤10^5,1≤A,B,C,K≤10^9。
cs 复制代码
#include <stdio.h>

int main() {
    int T;
    scanf("%d", &T);
    
    while (T--) {
        int a, b, c, k;
        int a1, b1, c1;
        int flag = 0;
        
        scanf("%d %d %d %d", &a, &b, &c, &k);
        
        for (int i = 1; i <= k; i++) {
            //用一个新的变量a1表示,这样可以避免计算b1时的a是上一次计算完了的
            a1 = (b + c) / 2;
            b1 = (a + c) / 2;
            c1 = (a + b) / 2;
            //但是肯定不能就光上面的式子就完成循环,所以要进行下面的赋值操作,这样才能保证下一次循环的a,b,c是新算出来的。
            a = a1;
            b = b1;
            c = c1;
            //这个要放在循环里边,因为有可能计算着,突然出现三个数相等的情况
            if (a == b && b == c) {
                printf("%d %d %d\n", a, b, c);
                flag = 1;
                break;
            }
        }
        //最后所有的循环都结束了,算出来的是最后一年的产值的时候,才进行输出。
        //同时标志变量表示没有出现过相等的情况,这样才进行输出
        if (!flag) {
            printf("%d %d %d\n", a, b, c);
        }
    }
    
    return 0;
}

上述题目其实挺简单,但我写的代码有点小瑕疵:

cs 复制代码
#include<stdio.h>
#include<stdlib.h>

int main() {
    int t;
    scanf("%d", &t);
    
    for (int i = 0; i < t; i++) {
        int a, b, c, k;
        scanf("%d%d%d%d", &a, &b, &c, &k);
        
        // 特殊情况:初始就相等
        if (a == b && b == c) {
            printf("%d %d %d\n", a, b, c);
            continue;
        }
        
        // 如果k为0,直接输出原始值
        if (k == 0) {
            printf("%d %d %d\n", a, b, c);
            continue;
        }
        
        // 动态分配数组
        int *arr_a = (int *)malloc((k + 1) * sizeof(int));
        int *arr_b = (int *)malloc((k + 1) * sizeof(int));
        int *arr_c = (int *)malloc((k + 1) * sizeof(int));
        
        if (!arr_a || !arr_b || !arr_c) {
            // 内存分配失败处理
            return 1;
        }
        
        // 初始化第0次的值
        arr_a[0] = a;
        arr_b[0] = b;
        arr_c[0] = c;
        
        // 计算k次调整
        for (int j = 0; j < k; j++) {
            // 注意:这里是整数除法,会自动向下取整
            arr_a[j + 1] = (arr_b[j] + arr_c[j]) / 2;
            arr_b[j + 1] = (arr_a[j] + arr_c[j]) / 2;
            arr_c[j + 1] = (arr_a[j] + arr_b[j]) / 2;
            
            // 如果已经相等,后面的都会相同,可以提前结束
            if (arr_a[j + 1] == arr_b[j + 1] && arr_b[j + 1] == arr_c[j + 1]) {
                // 填充剩余的部分为相同的值
                for (int m = j + 2; m <= k; m++) {
                    arr_a[m] = arr_a[j + 1];
                    arr_b[m] = arr_a[j + 1];
                    arr_c[m] = arr_a[j + 1];
                }
                break;
            }
        }
        
        printf("%d %d %d\n", arr_a[k], arr_b[k], arr_c[k]);
        
        // 释放内存
        free(arr_a);
        free(arr_b);
        free(arr_c);
    }
    return 0;
}

在洛谷上就是运行不对,因为k很大(比如10^9),我的代码会尝试分配巨大的数组,导致内存不足,所以不能用数组存储每一步。而正确的代码是每一次计算完成后就进行赋值操作,没有什么风险。

但如果非要用数组进行存储的话,下面的代码是可以的:

cs 复制代码
#include<stdio.h>
#include<stdlib.h>

int main() {
    int t;
    scanf("%d", &t);
    
    while (t--) {
        int a, b, c, k;
        scanf("%d%d%d%d", &a, &b, &c, &k);
        
        // 特殊情况处理
        if (k == 0) {
            printf("%d %d %d\n", a, b, c);
            continue;
        }
        
        // 只需要两个数组:当前状态和下一状态
        int *current = (int *)malloc(3 * sizeof(int));
        int *next = (int *)malloc(3 * sizeof(int));
        
        current[0] = a;
        current[1] = b;
        current[2] = c;
        
        for (int i = 0; i < k; i++) {
            next[0] = (current[1] + current[2]) / 2;
            next[1] = (current[0] + current[2]) / 2;
            next[2] = (current[0] + current[1]) / 2;
            
            // 检查是否已经收敛
            if (next[0] == next[1] && next[1] == next[2]) {
                // 直接设置最终结果
                current[0] = next[0];
                current[1] = next[0];
                current[2] = next[0];
                break;
            }
            
            // 交换指针,准备下一次迭代
            int *temp = current;
            current = next;
            next = temp;
        }
        
        // 注意:最后一次迭代后,结果在current中
        printf("%d %d %d\n", current[0], current[1], current[2]);
        
        free(current);
        free(next);
    }
    return 0;
}

上述代码内存效率高:

传统方法:需要k+1个数组元素,O(k)空间

本方法:只需要2×3=6个元素,O(1)空间

在本代码中,不再像传统方法一样分配的是每种矿的产值数组,而是将三种矿都分配在一个数组中,这个数组只表示当前的产值,而不像之前的代码数组表示k年的产值。

特别注意交换指针:

int *temp = current;

current = next;

next = temp;

cs 复制代码
过程图示
迭代前: current = [a, b, c], next = 空数组
迭代1: 计算next = [a1, b1, c1]
       交换:current指向next,next指向原来的current
迭代2: 计算next = [a2, b2, c2](使用current中的[a1, b1, c1])
       再次交换...
相关推荐
云和数据.ChenGuang2 小时前
python 面向对象基础入门
开发语言·前端·python·django·flask
空空空空空空空空空空空空如也2 小时前
QT通过编译宏区分x86 linux arm的方法
linux·开发语言·qt
独自破碎E2 小时前
【BISHI11】变幻莫测
android·java·开发语言
Gavin在路上2 小时前
SpringAIAlibaba之短期记忆与长期记忆实现原理(十一)
开发语言·人工智能
m0_706653232 小时前
C++中的解释器模式
开发语言·c++·算法
Cx330❀2 小时前
深入理解 Linux 基础 IO:从 C 库到系统调用的完整剖析
linux·运维·服务器·c语言·数据库·人工智能·科技
lsx2024062 小时前
命令模式:深入理解与实战应用
开发语言
西电研梦2 小时前
26西电考研 | 寒假开始,机试 or C语言程序设计怎么准备?
c语言·考研·华为od·研究生·西安电子科技大学·计算机408
CC.GG2 小时前
【Linux】基础I/O----C语言文件操作与系统调用文件操作
linux·c语言·网络