【题解】【一题多解】—— [NOIP1998 普及组] 三连击

[NOIP1998 普及组] 三连击

戳我查看题目(洛谷)

题目背景

本题为提交答案题,您可以写程序或手算在本机上算出答案后,直接提交答案文本,也可提交答案生成程序。

题目描述

1 , 2 , … , 9 1, 2, \ldots , 9 1,2,,9 9 9 9 个数分成 3 3 3 组,分别组成 3 3 3 个三位数,且使这 3 3 3 个三位数构成 1 : 2 : 3 1 : 2 : 3 1:2:3 的比例,试求出所有满足条件的 3 3 3 个三位数。

输入格式

输出格式

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

输入输出样例

输入 #1

输出 #1

192 384 576
* * *
...

* * *
(剩余部分不予展示)

提示

NOIP1998 普及组 第一题

解法1.直接提交答案

//建议大家不要这么做,做算法题是为了锻炼思维的。而不是一味地完成任务似的刷题。
#include<bits/stdc++.h>
using namespace std;
int main()
{
    cout<<"192 384 576"<<endl;
    cout<<"219 438 657"<<endl;
    cout<<"273 546 819"<<endl;
    cout<<"327 654 981"<<endl;
    return 0;
}

解法2.普通枚举

2.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的范围只需要从123~987

2.2.AC代码

#include<bits/stdc++.h>
using namespace std;
int pail[10];
void go(int x)//将三位数分解到桶里 
{
	pail[x%10]++;
	pail[x/10%10]++;
	pail[x/100]++;
}
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;
}
int main()
{
    int a,b,c;
    for(a=123;a<=987;a++)//枚举a 
    {
    	b=a*2;c=a*3;//通过a算出b和c 
    	if(check(a,b,c))//检查 
		    cout<<a<<' '<<b<<' '<<c<<endl;
	}
	return 0;
}

解法3.全排列枚举

3.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并判断就好了。

3.2.AC代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int num[15]={0,1,2,3,4,5,6,7,8,9},a,b,c,cnt=0;//数组初始化 
    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*2==b*1&&a*3==c*1)
            cout<<a<<' '<<b<<' '<<c<<endl; 
	}while(next_permutation(num+1,num+10));//重复生成下一个全排列 
	return 0;
}

解法4.深度优先搜索

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

4.1.题意分析

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

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

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

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

这判断条件都要把我写吐血了

4.2.AC代码

#include<bits/stdc++.h>
using namespace std;
bool is_have[10];//用is_have数组标记每个数是否出现 
vector<int>a;
void dfs(int step)
{
	if(step>9)//判断是否符合条件
	    if((a[6]*100+a[7]*10+a[8])*1.0/(a[0]*100+a[1]*10+a[2])==3&&
		   (a[3]*100+a[4]*10+a[5])*1.0/(a[0]*100+a[1]*10+a[2])==2)
		{
			for(int i=0;i<9;i++)
			{
				cout<<a[i];
				if((i+1)%3==0)
				    cout<<' ';//满三个数就输出一个空格
			}
			cout<<endl;
		}
	for(int i=1;i<=9;i++)
		if(!is_have[i])
		{
			a.push_back(i);//填空 
			is_have[i]=1;
			dfs(step+1);//填下一个空 
			a.pop_back();//恢复现场 
			is_have[i]=0;
		}
}
int main()
{
    dfs(1);//从1开始填 
	return 0;
}

好了,你可以去挑战洛谷 P1618 三连击(升级版)了。相信你能做到的!

喜欢就订阅此专辑吧!

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

蓝胖子教编程

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

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

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

打赏作者

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

抵扣说明:

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

余额充值