蓝桥杯2684数位排序满分题解

文章描述了一个关于数位之和排序的问题,其中给定正整数n和m,需要找到按数位之和排序后位于第m位置的数字。文中提出了使用快速选择算法在线性时间内解决该问题的方法,通过定义数位之和的比较函数并实现partition函数来定位目标元素的位置。
摘要由CSDN通过智能技术生成

题目描述

小蓝对一个数的数位之和很感兴趣,今天他要按照数位之和给数排序。当两个数各个数位之和不同时,将数位和较小的排在前面,当数位之和相等时,将数值小的排在前面。

例如,2022 排在 409 前面,因为 2022 的数位之和是 6,小于 409 的数位之和 13。

又如,6 排在 2022 前面,因为它们的数位之和相同,而 6 小于 2022。

给定正整数 n,m,请问对 1 到 n 采用这种方法排序时,排在第 m 个的元素是多少? 

输入格式

输入第一行包含一个正整数 n。

第二行包含一个正整数 m。

输出格式

输出一行包含一个整数,表示答案。

样例输入

复制

13
5

样例输出

复制

3

提示

1 到 13 的排序为:1, 10, 2, 11, 3, 12, 4, 13, 5, 6, 7, 8, 9。第 5 个数为 3。

对于 30% 的评测用例,1 ≤ m ≤ n ≤ 300。

对于 50% 的评测用例,1 ≤ m ≤ n ≤ 1000。

对于所有评测用例,1 ≤ m ≤ n ≤ 1e6。 

蓝桥杯2022年第十三届省赛真题-数位排序 - C语言网 

朴素解法:

定义大小关系

int get(int x) { 
	int res = 0; 
	while (x > 0) {
		res += x % 10;
		x /= 10;
	}
	return res;
}
bool ran(int a, int b) {//a是否大于b
	int xa = get(a);
	int xb = get(b);
	return xa > xb || (xa == xb && a > b);
}

接着排序后返回第m位数

sort(v.begin(),v.end(),[&](int a,int b){
    return ran(b,a);
});

 考虑数据量n最大1e6,感觉应该是过不了的

 题目要求的是返回第m大的数,如果简单排序,我们的工作是不是做太多了?

考虑线性时间选择

先引入一个函数

int partition(vector<int>& v, int lo, int hi) {
	int x = v[lo];
	int hole = lo;
	int l = lo + 1, r = hi;
	while (l <= r) { 
		while (l <= r && ran(v[r], x)) r--;
		if (l > r)break;
		v[hole] = v[r];
		hole = r;
		r--;
		while (l <= r && ran(x, v[l])) l++;
		if (l > r)break;
		v[hole] = v[l];
		hole = l;
		l++;
	}
	v[hole] = x;
	return hole;
}

了解快排的同志们应该很清楚,不了解的话请先阅读有关资料

partition可以将 下标 lo~hi 的元素 划分为两部分 A,v[hole],B

且存在 v[i] < v[hole] < v[j] 对任意 i∈A, j∈B成立

依靠此,我们就可以快速知道第m位数位于哪部分!!!

那我们就不需要处理另一部分

#include<iostream>
#include<vector>
#include <numeric>
#include<set>
#include <queue>
#include <unordered_map> 
#include<unordered_set>
#include<math.h> 
#include<algorithm>
#include<stack>
#include<string>
#define PI acos(-1) 
using namespace std;
typedef long long ll;
const ll INF = -1;
const ll mod = 1e9 + 7; 
int get(int x) { 
	int res = 0; 
	while (x > 0) {
		res += x % 10;
		x /= 10;
	}
	return res;
}
bool ran(int a, int b) {//a是否大于b
	int xa = get(a);
	int xb = get(b);
	return xa > xb || (xa == xb && a > b);
}
int partition(vector<int>& v, int lo, int hi) {
	int x = v[lo];
	int hole = lo;
	int l = lo + 1, r = hi;
	while (l <= r) { 
		while (l <= r && ran(v[r], x)) r--;
		if (l > r)break;
		v[hole] = v[r];
		hole = r;
		r--;
		while (l <= r && ran(x, v[l])) l++;
		if (l > r)break;
		v[hole] = v[l];
		hole = l;
		l++;
	}
	v[hole] = x;
	return hole;
}
int dfs(vector<int>& v, int idx, int lo, int hi) { 
	int x = partition(v, lo, hi);
	if (x == idx)return v[idx];
	if (x > idx) {
		return dfs(v, idx, lo, x - 1);
	}
	else return dfs(v, idx, x + 1, hi);
}
int main() { 
	int n, m;
	cin >> n >> m;
	vector<int>v(n);
	iota(v.begin(), v.end(), 1);
	cout << dfs(v, m - 1, 0, n - 1);
}
 

由于v一开始是1 2 3 4 ... n,partition比较不明显(慢一点)

可以在每次dfs的时候,交换 (v[lo], v[rand]),rand属于(lo,hi] 

我没交换但是也过了。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值