【题解】【枚举,全排列枚举】——三连击(升级版)

三连击(升级版)

通往洛谷的传送门

题目描述

1 , 2 , … , 9 1, 2,\ldots, 9 1,2,,9 9 9 9 个数分成三组,分别组成三个三位数,且使这三个三位数的比例是 A : B : C A:B:C A:B:C,试求出所有满足条件的三个三位数,若无解,输出 No!!!

输入格式

三个数, A , B , C A,B,C A,B,C

输出格式

若干行,每行 3 3 3 个数字。按照每行第一个数字升序排列。

输入输出样例

输入 #1

1 2 3

输出 #1

192 384 576
219 438 657
273 546 819
327 654 981

提示

保证 A < B < C A<B<C A<B<C


upd 2022.8.3 \text{upd 2022.8.3} upd 2022.8.3:新增加二组 Hack 数据。

0.前言

    我们虽然可以参考[NOIP1998 普及组] 三连击的做法,基本上都没有修改的地方。但是这里还是给大家讲解一下。

解法1.普通枚举

1.1.题意分析

    对于这道题,如果我们分别选择枚举abc,那么时间复杂都会达到惊人的 O ( n 3 ) O(n^3) O(n3)

    我们可以选择只枚举a, 通过a来算出bc

    那么接下来就只需要解决检查是否符合要求的问题了。在这里,我们可以使用类似于计数排序的方法。定义一个pail数组,pail[i]表示数字i出现的次数,我们可以称之为“桶”。如果某个数字没有出现,那么就是不符合要求。

    首先定义一个分解数字到“桶”里的go函数

int pail[10];
void go(int x)//将三位数分解到桶里
{
	pail[x%10]++;
	pail[x/10%10]++;
	pail[x/100]++;
}

    然后是检查是否符合要求的check函数

bool check(int a,int b,int c) 
{
	memset(pail,0,sizeof(pail));//初始化桶 
	go(a);go(b);go(c);//分解 
	for(int i=1;i<=9;i++)//如果有数没有出现就返回假 
	    if(!pail[i])
	        return 0;
	return 1;
}

注意:此题中的测试点中有A=0的情况,因此应该加一个特判。

1.2.AC代码

#include<bits/stdc++.h>
using namespace std;
int pail[10],is_have_ans;
void go(int x)//将一个三位数分解到桶里 
{
	pail[x%10]++;
	pail[x/10%10]++;
	pail[x/100]++;
}
bool check(int x,int y,int z)//检查
{
	memset(pail,0,sizeof(pail));
	go(x);go(y);go(z);
	for(int i=1;i<=9;i++)
	    if(!pail[i])
	        return 0;
	return 1;
}
int main()
{
    int a,b,c,A,B,C;
    cin>>A>>B>>C;
    if(A==0)//如果A为0绝对无解
    {
        cout<<"No!!!"; 
	    return 0;
    }
    for(a=123;a<=987;a++)
    {
    	if(a*B%A||a*C%A)continue;//如果有余数就不能构成比例
    	b=a*B/A;c=a*C/A;//算出第二个数和第三个数
    	if(check(a,b,c))
    	    cout<<a<<' '<<b<<' '<<c<<endl,is_have_ans=1;
	}
	if(!is_have_ans)//如果无解
	    cout<<"No!!!"; 
	return 0;
}

解法2.全排列枚举

2.1.题意分析

    因为abc总共拥有的数字都只有1~9,是不变的。所以可以考虑使用STL里的next_permutation函数。具体语法如下:

next_permutation(数组名+1,数组名+需要排列的长度+1);

    它的作用是生成一个排列的下一个字典序稍大的排列。如果这个排列已经是字典序最大的排列,返回0,否则返回1

    比如求123的全排列就可以这样写:

int num[]={0,1,2,3};//这里初始化要多加一个0,用来占位
do
{
    cout<<num[1]<<num[2]<<num[3]<<endl;
}while(next_permutation(num+1,num+4));

输出:

123
132
213
231
312
321

如果还不懂可以拿洛谷 P1088 [NOIP2004 普及组] 火星人练练手。

    那么我们只需要定义一个数组num,并不断生成它的下一个排列,再分别计算abc并判断就好了。

2.2.AC代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int A,B,C,num[15]={0,1,2,3,4,5,6,7,8,9},a,b,c,is_have_ans=0;//数组初始化 
    cin>>A>>B>>C;
    do
    {
    	a=num[1]*100+num[2]*10+num[3];//计算三个数 
    	b=num[4]*100+num[5]*10+num[6];
    	c=num[7]*100+num[8]*10+num[9];
        if(a*B==b*A&&a*C==c*A)//避免除以0的小技巧 
            cout<<a<<' '<<b<<' '<<c<<endl,is_have_ans=1;
	}while(next_permutation(num+1,num+10));//重复生成下一个全排列 
	if(!is_have_ans)//没有一个解 
	    cout<<"No!!!";
	return 0;
}

解法3.深度优先搜索

    如果还没学搜索的同学看完上面就已经够用了。如果想要练习深度优先搜索可以看看我下面的解法。

3.1.题意分析

    众所周知,搜索是优雅的暴力枚举

    这里跟上面的策略一样,使用一个is_have数组作为“桶”。然后使用一个可变数组vector来储存abc

不知道vector是什么的同学就把它当普通数组就好了。学有余力的同学也可以把代码改成使用普通数组

    然后直接套dfs模版就行了。

3.2.AC代码

#include<bits/stdc++.h>
using namespace std;
int A,B,C,pail[10],is_have_ans;
vector<int>v;
bool check()//检查是否符合要求 
{
	memset(pail,0,sizeof(pail));//初始化 
	for(int i=0;i<9;i++)//将每个出现的数标记下来 
	    pail[v[i]]=1;
	for(int i=1;i<=9;i++)//判断是否有数字没有出现 
	    if(!pail[i])
	        return 0;
	int a=v[0]*100+v[1]*10+v[2];
	int b=v[3]*100+v[4]*10+v[5];
	int c=v[6]*100+v[7]*10+v[8];
	if(b*1.0/B*A==a&&c*1.0/C*A==a)//都出现了而且符合A:B:C 
	    return 1;
	return 0;
}
void dfs(int step)
{
	if(step>9)
		if(check())//如果符合要求 
		{
			for(int i=0;i<9;i++)//重复输出 
			{
				cout<<v[i];
				if((i+1)%3==0)
				    cout<<' ';
			}
			is_have_ans=1;//出现了解,不用输出No!!!
			cout<<endl;
			return;
		}
	for(int i=1;i<=9;i++)
	    if(!pail[i])//如果这个数还没出现 
	    {
	    	v.push_back(i);
	    	pail[i]=1;
	    	dfs(step+1);//递归处理接下来的数 
	    	v.pop_back();
	    	pail[i]=0;
		}
	return;
}
int main()
{
    cin>>A>>B>>C;
    dfs(1);
    if(!is_have_ans)//如果没有出现任何一个解 
        cout<<"No!!!";
	return 0;
}

喜欢就订阅此专辑吧!

【蓝胖子编程教育简介】
蓝胖子编程教育,是一家面向青少年的编程教育平台。平台为全国青少年提供最专业的编程教育服务,包括提供最新最详细的编程相关资讯、最专业的竞赛指导、最合理的课程规划等。本平台利用趣味性和互动性强的教学方式,旨在激发孩子们对编程的兴趣,培养他们的逻辑思维能力和创造力,让孩子们在轻松愉快的氛围中掌握编程知识,为未来科技人才的培养奠定坚实基础。

欢迎扫码关注蓝胖子编程教育
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝胖子教编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值