题目描述
偏远的小镇上,三兄弟共同经营着一家小型矿业公司"兄弟矿业"。公司旗下有三座矿山:金矿、银矿和铜矿,它们的初始产值分别用非负整数 A、B 和 C 表示。这些矿山的产出是小镇经济的核心,支撑着三兄弟和许多矿工家庭的生计。
然而,各矿山的产值波动剧烈,有时金矿收益高而银矿、铜矿低迷,有时则相反。这种不稳定性让公司收入难以预测,也常引发兄弟间的争执。为了稳定经营,三兄弟设计了一个公平的产值调整策略,每年执行一次,每次调整时,将根据当前的产值 A、B、C,计算新产值:
- 金矿新产值:A′=⌊(B+C)/2⌋;
- 银矿新产值:B′=⌊(A+C)/2⌋;
- 铜矿新产值: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])
再次交换...