题目描述
小蓝对一个数的数位之和很感兴趣,今天他要按照数位之和给数排序。当两个数各个数位之和不同时,将数位和较小的排在前面,当数位之和相等时,将数值小的排在前面。
例如,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。
朴素解法:
定义大小关系
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]
我没交换但是也过了。。。。