P1618 三连击(升级版)

😊😊 😊😊
不求点赞,只求耐心看完,指出您的疑惑和写的不好的地方,谢谢您。本人会及时更正感谢。希望看完后能帮助您理解算法的本质
😊😊 😊😊

题目描述:

三连击(升级版)

题目描述

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 数据。

小白到进阶各种解法:

一、暴搜:😊

在这里插入图片描述

思路:

  1. 经过我研究分析得到,本题本质是一道 递归实现组合型枚举。两道题的区别如下图所示:
    在这里插入图片描述
    下面先给出组合型枚举的代码:即从n个数中选出m个数,输出所有可能的方案:
    递归搜索每一种组合,从n个数选m个,那么 n>=m,则m个位置必然不为空,对于已经用过的数就不能再用了,所以标记为true,然后递归下一个位置,然后对于下一个位置,只有 n − 1 n-1 n1 个数去选。直到 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,…],当你继续往下走的时候你会发现就又回到了递归升序!

总结本题思路:

  1. 递归从9个数中选出3个数为一对的所有组合,实际上就是从9个数中选出9个数的不同组合,我们将每个数都存在一个一维数组里面。
  2. 当9个数都确定好了以后,则将其3个一对转化成一个整数,然后判断即可:
  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;
}

在这里插入图片描述

二、记忆化搜索:待更新😊

在这里插入图片描述

代码:


在这里插入图片描述

三、本题考察算法:😊

代码:



在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值