洛谷P1008(三连击[NOIP 1998 普及组])题解

题目大意:把1到9这9个数字分成三组,组成三个三位数,这三个数要满足1:2:3的比例。

首先,我们需要确定这三个数的范围。因为都是三位数,所以第一个数x至少是100,最大的三位数是999。但是第三个数是3x,所以3x必须小于等于999。因此x的最大可能值是999/3=333。所以x的取值范围应该在100到333之间。不过,可能还可以更精确一些。比如说,当x=333的时候,3x=999,这是可以的。但实际可能更小的范围,比如当x=192的时候,3x=576,这时候三个数分别是192、384、576,刚好满足条件。

接下来,我需要遍历可能的x值,从123到329之间。为什么是329呢?因为当x=329时,3x=987,这时候可能还有足够的数字不被重复使用。不过具体范围可能需要进一步分析。比如,当x超过333的话,3x会超过三位数,所以x的上限确实是333。但比如当x=192的时候,例子中的情况是正确的。所以正确的x范围应该是100到333之间的那些数,使得3x仍然是三位数,并且所有数字都不重复。

然后,对于每一个可能的x,我需要生成对应的2x和3x,然后检查这三个数是否使用了1到9的所有数字,且每个数字只出现一次。比如,这三个数的每一位数字应该刚好覆盖1到9,不重复也不遗漏。

具体来说,步骤如下:

1. 遍历x从123到333,因为3x最多是999。

2. 对每个x,计算y=2x,z=3x。

3. 将x、y、z这三个数的每一位数字提取出来,合并成一个字符串或集合。

4. 检查这个集合是否包含1到9的所有数字,且没有0,并且总共有9个不同的数字。

5. 如果满足条件,就输出这三个数。

这里需要注意几点:比如,每个数必须是三位数,所以x至少是100,而y=2x可能超过三位数吗?比如当x=334时,3x=1002,超过三位数,所以x的上限确实是333。因此,x的范围是100到333之间的整数。

然后,如何提取每个数的各位数字?比如,x=192,那么各位数字是1、9、2。同样,y=384的各位是3、8、4,z=576的各位是5、7、6。合并起来正好是1-9,没有重复。

接下来,如何高效地检查这九个数字是否满足条件?可以使用一个数组或哈希集合来记录每个数字是否出现过。比如,可以用一个长度为10的数组(索引0到9),初始化为0,然后遍历每个数字,将对应的位置标记为1。最后检查数组1到9的位置是否都是1,且总和是9。或者,可以把所有数字合并成一个字符串,然后排序后是否等于“123456789”。

例如,将x、y、z的各位合并成一个字符串,然后排序,如果等于"123456789"则符合条件。

例如,x=192,y=384,z=576,合并后的数字是1,9,2,3,8,4,5,7,6,排序后就是123456789。

这样,处理起来就比较简单了。具体步骤可以是:

对于每个x,生成y=2x,z=3x:

- 检查这三个数是否都是三位数。例如,当x=100时,y=200,z=300,这时候合并后的数字中有0,所以不符合条件。所以必须确保每个数的各位都不是0。

所以,在生成x的时候,应该确保x的各位数字都不为0,并且同样地检查y和z的各位是否有0。或者,在合并所有数字后,检查是否包含0。如果有的话,直接跳过。

比如,x=105,那么它的各位是1、0、5,包含0,所以即使y和z中没有0,整个合并后的数字也会包含0,不符合条件。因此,在遍历x的时候,可以首先检查x本身的各位是否有0,如果有的话,直接跳过,这样可以减少计算量。

所以,具体步骤如下:

遍历x从123到333:

- 检查x是否由三个不同的非零数字组成。

- 计算y=2x,检查y是否为三位数,并且各位没有0。

- 计算z=3x,检查z是否为三位数,并且各位没有0。

- 合并三个数的各位数字,检查是否刚好是1-9,每个数字出现一次。

或者,另一种方法是,不管中间是否有0,直接合并所有数字,然后检查是否存在0,或者是否总共有9个不同的数字且刚好是1-9。

例如,对于每个x,生成三个数,然后合并所有数字:

- 将x、y、z的每一位数字提取出来,组成一个数组。

- 检查这个数组的长度是否为9(即三个三位数共有9位)。

- 检查是否每个数字都是1-9中的,且没有重复。

但这里需要注意,当x本身含有重复数字时,比如x=112,那么它的各位是1、1、2,这时候合并后的数组会有重复,所以可以直接跳过。

但这样处理可能比较麻烦。或者,不管如何,合并所有数字后,进行排序,然后比较是否等于"123456789"。

例如,假设将三个数的所有数字合并成一个字符串,然后排序,如果等于"123456789",就满足条件。

例如,假设x=192,y=384,z=576:

合并后的字符串是"192384576",拆分成每个字符,排序后得到['1','2','3','4','5','6','7','8','9'],即符合条件。

这个方法比较直接,且容易实现。代码中可以使用字符串操作。

现在,如何生成这三个数的各位数字?

在C++中,可以将每个数转换为字符串,然后拼接起来,形成一个长度为9的字符串。然后检查每个字符是否是唯一的,并且没有0,且包含1-9的所有数字。

例如,对于x=192,y=384,z=576,字符串是"192384576",然后遍历每个字符,检查是否包含所有1-9的数字,没有重复,且没有0。

或者,更高效的方式是将这个字符串排序,然后比较是否等于"123456789"。

例如:

string s = to_string(x) + to_string(y) + to_string(z);

sort(s.begin(), s.end());

if(s == "123456789") 则符合条件。

这样就可以快速判断是否满足条件。这样处理的优点是代码简洁,容易实现。

现在,我需要考虑如何生成所有可能的x。x的范围是123到333,且x必须满足3x <= 999,所以x的最大值是333。但x本身必须是三位数,所以最小值是100。但是当x=100时,y=200,z=300,合并后的数字中有多个0,显然不符合条件。所以实际可能的x会更小。或者,让程序自行处理,因为当x=100时,生成的字符串会包含0,排序后不会等于"123456789",所以会被过滤掉。

所以,代码的大致流程是:

for x in 123..333:

y = 2*x

z = 3*x

s = to_string(x) + to_string(y) + to_string(z)

sort(s.begin(), s.end())

if s == "123456789":

输出x, y, z

但需要注意,x、y、z是否都是三位数?比如,当x=333时,3x=999,没问题。而当x= 167时,y=334,z=501,这时可能各位是否有重复?

比如,比如x= 123,那么y=246,z=369,合并后的字符串是123246369。排序后是 1 2 3 2 3 4 6 6 9 → 显然不满足条件,所以会被过滤掉。

所以,这样的遍历方式是正确的,但可能效率不高,因为有很多x会被排除。但是因为x的范围只有333-123=210个数左右,所以计算量非常小,不会有问题。

现在,我需要确定x的范围是否正确。例如,是否存在x= 333的情况?比如,当x=333时,3x=999,但此时合并后的数字是333 666 999,显然有很多重复,所以会被过滤掉。而正确的例子如192、384、576,它们的x是192,所以x的范围必须足够大才能包含这些情况。

所以,正确的x的范围应该是从123到333。但是有没有可能更小呢?例如,当x= 100时,y=200,z=300,合并后的数字含有0,会被过滤掉。而正确解中的x=192,所以x的遍历必须包括这个数值。

所以,遍历x从100到333,然后用条件判断过滤掉不符合的情况,这样虽然会有一些无效循环,但由于总数只有234个左右,计算量不大。

因此,代码可以这样写:

for (int x = 100; x <= 333; ++x) {

int y = 2 * x;

int z = 3 * x;

string s = to_string(x) + to_string(y) + to_string(z);

sort(s.begin(), s.end());

if (s == "123456789") {

// 输出x y z

}

}

但是,这样可能有一个问题:比如,当x= 112时,y=224,z=336,合并后的数字是112224336,其中包含重复的数字,如1出现多次,2出现多次,3出现多次,所以排序后的字符串会比"123456789"长,或者包含重复字符,所以不会满足条件。

所以这个方法是可行的。

现在,测试一下例子中的输出是否正确。比如,当x=192时:

x=192 → y=384 → z=576 → 字符串是 "192384576",排序后是 123456789,符合条件。所以会被输出。这是正确的。

另一个可能的解是x=219,y=438,z=657。合并后的数字是219438657 → 排序后得到123456789? 219的各位是2、1、9,438的各位是4、3、8,657的各位是6、5、7。合并后的数字是2,1,9,4,3,8,6,5,7 → 排序后是123456789。所以这也是一组解。

第三个可能的解是x=273,y=546,z=819 → 各位是2、7、3,5、4、6,8、1、9 →合并后是273546819 → 排序后123456789。所以这也是一组解。

所以正确的输出应该有三个组合,例如:

192 384 576

219 438 657

273 546 819

但根据题目给出的样例输出,只给出了一行,可能只是示例。或者可能题目中的输出有多个符合条件的解。比如,可能还有其他解吗?

或者,可能这三个是唯一的解。需要检查一下。

例如,当x= 327时,y=654,z=981。各位数字是3、2、7;6、5、4;9、8、1。合并后的数字是3,2,7,6,5,4,9,8,1 →排序后是123456789。所以这也是一个解。那么这个时候,x=327是否符合x的范围?因为327<=333,是的。所以这应该也是一个解。

那这个时候,为什么样例输出中没有显示?可能我之前的例子中的解可能不止三个。

那这个时候,代码应该输出所有符合条件的x、y、z。因此,正确的解可能包括:

比如,当x= 192 → 192 384 576

x= 219 → 219 438 657

x= 273 → 273 546 819

x= 327 → 327 654 981

这四个解。或者是否还有其他?

例如,x= 129,y=258,z=387 →合并后的数字是否满足?129 258 387 → 各位是1、2、9;2、5、8;3、8、7。合并后会有重复的2和8,所以排序后无法得到123456789。所以这组不成立。

那么,正确的解应该有哪些呢?

根据我的计算,这四个解是正确的:

192 384 576

219 438 657

273 546 819

327 654 981

这四个解都满足条件吗?

检查x=327时,3x=981,是的。合并后的各位是3、2、7;6、5、4;9、8、1。所有数字是1、2、3、4、5、6、7、8、9,没有重复,所以是的。

所以代码应该输出这四个解。或者可能还有其他?

比如,x= 146,y=292,z=438。检查各位是否重复:

146的各位是1、4、6;292的各位是2、9、2(重复了2,所以这组无效)。

所以,这说明在遍历x时,必须确保生成的y和z都是三位数,并且x、y、z的各位数字不重复,且合并后覆盖1-9。

所以,正确的代码应该能输出所有可能的解。

现在,回到代码实现的问题。在C++中,如何将整数转换为字符串并拼接?

例如,使用to_string函数:

int x = 192;

string s = to_string(x) + to_string(2*x) + to_string(3*x);

这会得到"192384576",然后排序后的字符串是"123456789",所以条件满足。

现在,测试x=327的情况:

x=327 → y=654 → z=981 →合并后的字符串是"327654981"。拆分为3,2,7,6,5,4,9,8,1 →排序后得到123456789。正确。

因此,代码应该能够找到这四个解。

现在,问题在于如何确定x的遍历范围是否正确。比如,x=327是否符合x <=333的条件?是的,因为327 <333,所以会被遍历到。

那现在,代码是否能够正确生成这四个解?

是的。例如,当x=192、219、273、327时,代码会生成对应的字符串,排序后满足条件,从而输出。

因此,正确的代码应该输出这四个解。

现在,检查样例的输出是否给出这些解。题目中的样例输出只给出了一行,但可能实际正确的解是四个。例如,根据一些参考资料,正确的输出应该是:

192 384 576

219 438 657

273 546 819

327 654 981

这四个解。所以代码应该生成这四个。

现在,回到代码的实现,编写C++代码:

需要循环x从100到333,对于每个x,生成y=2x,z=3x,并检查是否满足条件。

然后,将这三个数转换为字符串,拼接,排序后检查是否等于"123456789"。

注意,如果x、y、z中存在0的话,合并后的字符串会包含0,这样排序后的字符串就不会等于"123456789",所以会被排除。

所以,这样的处理是正确的。

现在,编写代码:

#include <iostream>

#include <string>

#include <algorithm>

using namespace std;

int main() {

for (int x = 100; x <= 333; ++x) { // x最大是333,因为3*333=999

int y = 2 * x;

int z = 3 * x;

string s = to_string(x) + to_string(y) + to_string(z);

sort(s.begin(), s.end());

if (s == "123456789") {

cout << x << " " << y << " " << z << endl;

}

}

return 0;

}

运行这段代码,看看是否输出正确的四个解。

测试:

当x=192 → 192*2=384 → 192*3=576 →合并后的字符串排序后是123456789 →输出。

同样,x=219 → 219*2=438 → 657 → yes.

x=273 → 546 → 819 → yes.

x=327 → 654 → 981 → yes.

这四个x值在循环范围内,所以会被遍历到。

因此,这段代码应该输出这四个解。

综上,这个问题的解决思路是通过遍历可能的第一个数x,生成对应的2x和3x,然后检查这三个数的各位数字是否恰好覆盖1到9,每个数字只出现一次。通过将这三个数的各位数字合并、排序后与"123456789"比较,可以快速判断是否满足条件。最终输出所有符合条件的三个数组合。

AC代码:

#include <iostream>
#include <string>
#include <algorithm>

using namespace std;

int main() {
    for (int x = 123; x <= 329; ++x) {  // 最大x为329(3x=987)
        int y = 2 * x;
        int z = 3 * x;
        string s = to_string(x) + to_string(y) + to_string(z);
        sort(s.begin(), s.end());
        if (s == "123456789") {
            cout << x << " " << y << " " << z << endl;
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值