【蓝桥杯】带分数。全排列 + 巧妙判断

原题

试题 历届试题 带分数 http://lx.lanqiao.cn/problem.page?gpid=T26

问题描述

100 可以表示为带分数的形式:100 = 3 + 69258 / 714。

还可以表示为:100 = 82 + 3546 / 197。

注意特征:带分数中,数字1~9分别出现且只出现一次(不包含0)。

类似这样的带分数,100 有 11 种表示法。

输入格式

从标准输入读入一个正整数N (N<1000*1000)

输出格式

程序输出该数字用数码1~9不重复不遗漏地组成带分数表示的全部种数。

注意:不要求输出每个表示,只统计有多少表示法!

样例输入1

100

样例输出1

11

样例输入2

105

样例输出2

6

分析

1、实现全排列

9个数字不重复,自然想到了全排列。9!=362 880。这个穷举数量是1秒内是可以完成的。这里我直接通过 递归+标记 的方式实现全排列。当然,下面参考博客也提供了一种 不易理解 的实现方式,效率好像稍微高那么一点点。

2、巧妙判断是否为满足题意的等式

得到全排列的一种序列后,如何判断这个序列所对应的等式满足题意呢?

序列共有9位,我们用num[]数组来装,num[0] ~ num[8]。

(1)由于a <= n,所以 a的位数 <= n的位数

(2)yizhi我们根据(1)的条件,枚举a。又因为序列的最后一位 num[8],也是 c的最后一位。这样我们就可以推出 b的最后一位是: bLast = ((n-a) * num[8]) % 10;

(3)知道了b的最后一位,那么我们就可以在 a 的后面(起始位置可以更准确,因为 b的位数 >= c的位数)找是否存在 bLast。如果存在,则通过判断  (b%c == 0 && n == a + b/c) 是否满足题意。

参考博客:http://lx.lanqiao.cn/problem.page?gpid=T26

效率

代码 

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;

int n;
int len = 0;

int num[15]; // 存放9个数字的全排列
int vis[15]; // 标志数字在之前是否已使用
int ans = 0;

// [start, end)
int getNum(int start, int end) {
	int t = 0;
	// 1 2 3
	for(int i = start; i < end; i++) {
		t = 10*t + num[i];
	}
		
	return t;
}

void print() {
	for(int i = 0; i < 9; i++)
		printf("%d ", num[i]);
	printf("\n");
}

void dfs(int k) { // 0
	if (k >= 9) {
//		print();
		
		// 得到一个全排列
		for(int i = 1; i <= len; i++) { // a的长度必定在[1, len]
			int a = getNum(0, i);
			// n = a + b / c   =>  b = (n - a) * c
			int bLast = ((n - a)*num[8]) % 10; // b的最后一位
			
			for(int j = (i-1)+ceil((9-i)/2); j < 8; j++) {
				
				if(num[j] == bLast) {
					int b = getNum(i, j+1);
					int c = getNum(j+1, 9);
					
					if(b%c == 0 && n == a + b/c) {
						ans++;	
					}
					
					break;  // 可以提高效率 
				}
			} 
		}

		return;
	}

	for(int i=1; i<=9; i++)
		if(vis[i]==0) { // 数字未被使用
			num[k]=i;  // 填入
			vis[i]=1; // 标记已被使用

			dfs(k+1); // 搜索下一个位置

			num[k]=0;
			vis[i]=0; // 解除标记
		}


}

int main() {
	scanf("%d", &n);

	// 计算n有多少位,a的位数不可能多比n的位数
	int t=n;
	while(t!=0) {
		len++;
		t /= 10;
	}
	
	dfs(0);
	printf("%d\n", ans);

	return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值