😊😊 😊😊
不求点赞,只求耐心看完,指出您的疑惑和写的不好的地方,谢谢您。本人会及时更正感谢。希望看完后能帮助您理解算法的本质
😊😊 😊😊
题目描述:
三连击(升级版)
题目描述
将
1
,
2
,
…
,
9
1, 2,\ldots, 9
1,2,…,9 共
9
9
9 个数分成三组,分别组成三个三位数,且使这三个三位数的比例是
A
:
B
:
C
A:B:C
A:B:C,试求出所有满足条件的三个三位数,若无解,输出 No!!!
。
//感谢黄小U饮品完善题意
输入格式
三个数, A , B , C A,B,C A,B,C。
输出格式
若干行,每行 3 3 3 个数字。按照每行第一个数字升序排列。
样例 #1
样例输入 #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 数据。
小白到进阶各种解法:
一、暴搜:😊
思路:
- 经过我研究分析得到,本题本质是一道 递归实现组合型枚举。两道题的区别如下图所示:
下面先给出组合型枚举的代码:即从n个数中选出m个数,输出所有可能的方案:
递归搜索每一种组合,从n个数选m个,那么 n>=m,则m个位置必然不为空,对于已经用过的数就不能再用了,所以标记为true,然后递归下一个位置,然后对于下一个位置,只有 n − 1 n-1 n−1 个数去选。直到 m 个位置逐步为空为止!
组合型枚举的代码:
#include<iostream>
#include<ctime>
#include<cstdio>
using namespace std;
const int N = 1e2 + 10;
int n, m;
int path[N];
bool used[N];
int random(int n)
{
return rand()*1ll*rand() % n;
}
void dfs (int u, int last)
{
if (u == m+1)
{
for (int i=1; i <= m; i ++)
cout << path[i] << " ";
puts("");
return ;
}
for (int i=last; i <= n; i ++) //循环枚举这n个数!
{
if (!used[i])
{
used[i] = true;
path[u] = i;
dfs (u + 1, i + 1);
used[i] = false;
}
}
}
int main()
{
srand((unsigned)time(0));
cin >> n >> m;
dfs (1, 1); //从第一个位置开始递归
return 0;
}
现在回到本题中来,本题是在组合型枚举的基础之上又加入了其他条件。但是只要剖析出来时组合型枚举就没有问题。从9个数中选3组,每组3个数,那么第一组等价于从9个数中选3个数,依次类推,但要记得标记每个数是否已经被用过了!另外递归实现组合型枚举中还利用了 不降原则,从而避免重复枚举的情况!
补充:
不降序原则:
[1 2 3]
[1 3 2]
两者是同一种方案,那么为了去重,我们可以采用升序,来枚举所有的方案,即所选序列只允许单调上升,这种方案一定是唯一的,不会出现重复!
不降原则的实现:即一个简单的last变量,记录它的上一位是x,然后回溯到x的时候,就从x开始往后遍历就完事了,比如上述的两个例子,
其实不止回溯,还有递归的放置方案的时候也保证了是升序的。 递归升序:[1, 2, _ ]
现在放置第三个元素,那么由于last=2,递归后,last=2,for循环中 i = last+1,即从3开始了 ! 回溯升序:当你从
[1, 2, 3] 回溯到:[1, _ _]的时候,即回到第二个位置的递归函数时,last = 2,那么随着 i ++,则只能往
3的方向走!不会什么 [1, 1,…],当你继续往下走的时候你会发现就又回到了递归升序!
总结本题思路:
- 递归从9个数中选出3个数为一对的所有组合,实际上就是从9个数中选出9个数的不同组合,我们将每个数都存在一个一维数组里面。
- 当9个数都确定好了以后,则将其3个一对转化成一个整数,然后判断即可:
- 判断方式:a:b = A:B — Ab == ba,同理其他的也一样!
本题AC代码:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 11;
int path[N];
bool used[N]; //标记这个数是否被用过了
int n;
bool ans;
int a, b, c;
int merge(int m)
{
int sum=0;
for (int i=3*m-2; i <= 3*m; i ++)
{
sum *= 10;
sum += path[i];
}
return sum;
}
void dfs(int u)
{
if (u == 10) //9个位置都已经枚举完毕了,现在每3个一组组成A, B , C;判断是否符合题目的比例!
{
//a:b = A:B ==> A*b == a*B;
//a:b:c == 1:2:3
if (merge(1)*b==merge(2)*a && merge(3)*a == merge(1)*c ){
ans = true;
cout << merge(1) << " " << merge(2) << " " << merge(3) << endl;
}
return ;
}
for (int i=1; i <= 9; i ++)
{
if (!used[i])
{
used[i] = true;
path[u] = i;
dfs(u+1);
used[i] = false;
}
}
}
int main()
{
cin >> a >> b >> c;
dfs(1);
if(!ans) cout << "No!!!";
return 0;
}
二、记忆化搜索:待更新😊
代码:
三、本题考察算法:😊
代码: