【题解】—— [NOIP1998 普及组] 三连击
- [NOIP1998 普及组] 三连击
- 题目背景
- 题目描述
- 输入格式
- 输出格式
- 输入输出样例
- 输入 #1
- 输出 #1
- 提示
- 解法1.直接提交答案
- 解法2.普通枚举
- 2.1.题意分析
- 2.2.AC代码
- 解法3.全排列枚举
- 3.1.题意分析
- 3.2.AC代码
- 解法4.深度优先搜索
- 4.1.题意分析
- 4.2.AC代码
[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.题意分析
对于这道题,如果我们分别选择枚举a
,b
,c
,那么时间复杂都会达到惊人的
O
(
n
3
)
O(n^3)
O(n3)。虽然本质上还是常数级别复杂度
我们可以选择只枚举a
, 通过a
来算出b
和c
。
那么接下来就只需要解决检查是否符合要求的问题了。在这里,我们可以使用类似于计数排序的方法。定义一个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.题意分析
因为a
,b
,c
总共拥有的数字都只有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
,并不断生成它的下一个排列,再分别计算a
,b
,c
并判断就好了。
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
来储存a
,b
,c
。
不知道
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;
}
喜欢就订阅此专辑吧!
【蓝胖子编程教育简介】
蓝胖子编程教育,是一家面向青少年的编程教育平台。平台为全国青少年提供最专业的编程教育服务,包括提供最新最详细的编程相关资讯、最专业的竞赛指导、最合理的课程规划等。本平台利用趣味性和互动性强的教学方式,旨在激发孩子们对编程的兴趣,培养他们的逻辑思维能力和创造力,让孩子们在轻松愉快的氛围中掌握编程知识,为未来科技人才的培养奠定坚实基础。
欢迎扫码关注蓝胖子编程教育