【AcWing每日一题】4653. 数位排序

简介: 【AcWing每日一题】4653. 数位排序

小蓝对一个数的数位之和很感兴趣,今天他要按照数位之和给数排序。


当两个数各个数位之和不同时,将数位和较小的排在前面,当数位之和相等时,将数值小的排在前面。


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


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


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

输入格式

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

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

输出格式

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

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

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

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

输入样例:

13
5

输出样例:

3

样例解释

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

第 5 个数为 3。


我的思路:

  • 对于50%的样例,是可以用暴力枚举的
  • 首先,开一个长度为106的数组,用来存储排序后的每一个数
  • 这里采用输入的同时排序,时间复杂度是够用的
    排序使用可以降低复杂度的二分算法
  • 排好之后直接输入m位置上的数即可

对于50%样例的代码:

#include<iostream>
using namespace std;
const int N = 1e6+10;
int a[N];
int position(int i, int len){
  if(len == 0) return 1;
  int sum = 0, cnt = i;
  while(cnt){
    sum += cnt%10;
    cnt /= 10;
  }
  int low = 1, high = len, mid;
  while(low <= high){
    mid = (low+high)/2;
    int sum_mid = 0, cnt_mid = a[mid];
    while(cnt_mid){
      sum_mid += cnt_mid%10;
      cnt_mid /= 10;
    }
    if(sum > sum_mid || (sum == sum_mid && i > a[mid])) 
      low = mid+1;
    else if(sum < sum_mid || (sum == sum_mid && i < a[mid])) 
      high = mid-1;   
  }
  return low;
}
int main(){
  int n, m, k = 0;
  cin >> n >> m;
  for(int i = 1; i <= n; i++, k++){
    int index = position(i, i-1);
    for(int j = i-1; j >= index; j--){
      a[j+1] = a[j];
    }
    a[index] = i;
  }
  cout << a[m];
  return 0;
}

查找位置的复杂度为O(nlog2n),所有总的复杂度为O(n2log2n),对于所有的样例肯定会TLE

优化思路:

  • 对于上面的106长度的数组,进行改进
  • 由于所有数所能产生的数位和的数量是有限的,所以可以考虑用散列表的形式进行存储
    对于每一个数,用所得到的数位和作为散列地址
  • 利用拉链法,使得相同数位和的数(同义词)存储在同一散列地址的链表下
  • 对于同一个数位和的不同数,后面来的一定比前面来的要大,所以可以不用链表,直接用数组

代码:(可以AC)

#include<iostream>
#include<vector>
#include<map>
using namespace std;
map<int, vector<int> > dic;
int main(){
  int n, m, k = 0, len = 0;
  cin >> n >> m;
  for(int i = 1; i <= n; i++){
    int cnt = i, sum = 0;
    while(cnt){
      sum += cnt%10;
      cnt /= 10;
    }
    if(dic.find(sum) == dic.end()){
      dic[sum] = vector<int>(0);
    }
    dic[sum].push_back(i);
  } 
  //对于map的遍历有点忘了
  //下面这段代码来源:https://www.acwing.com/solution/content/159370/ 
  for (const auto &i: dic){
        if (m > i.second.size())    m -= i.second.size();
        else{
            cout << i.second.at(m - 1);
            break;
        }
    }
  return 0;
} 

y总的思路:

  • 基本上算是用时间换空间,加一个排序、查找
  • 对于每一个数,存储下这个数的数位和
  • 根据这个数位和对1~n进行排序
  • 最后只需查找出第m个位置的数

代码:

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1000010;
int n, m;
int w[N], s[N];
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ )
    {
        w[i] = i;
        for (int j = i; j; j /= 10)
            s[i] += j % 10;
    }
    sort(w + 1, w + n + 1, [&](int a, int b) {
        if (s[a] != s[b]) return s[a] < s[b];
        return a < b;
    });
    printf("%d\n", w[m]);
    return 0;
}
//作者:yxc
//链接:https://www.acwing.com/activity/content/code/content/5088776/
//来源:AcWing
//著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

提示: 对于这道题的数据范围,如果在排序的同时进行计算数位和,会使得计算量上升一个量级。因为最快的排序也需要O(nlogn)的时间复杂度,所以需要先存下所有数的数位和,不然会TLE。

相关文章
蓝桥杯:桶排序 与 例题:算式问题
蓝桥杯:桶排序 与 例题:算式问题
91 0
|
7月前
|
索引
力扣每日一题 6/27 字符串 贪心
力扣每日一题 6/27 字符串 贪心
37 0
|
7月前
力扣每日一题 6/22 字符串/贪心
力扣每日一题 6/22 字符串/贪心
34 0
|
存储 人工智能 测试技术
【AcWing每日一题】4644. 求和
【AcWing每日一题】4644. 求和
79 0
|
存储 算法 C语言
【C语言刷题】猜名次、猜凶手、杨辉三角、杨氏矩阵、字符串左旋、判断是否为左旋子串
【C语言刷题】猜名次、猜凶手、杨辉三角、杨氏矩阵、字符串左旋、判断是否为左旋子串
84 0
|
人工智能 算法 测试技术
【AcWing每日一题】4655. 重新排序
【AcWing每日一题】4655. 重新排序
64 0
|
Java Python
leetcode每日一题.445:两数相加II
leetcode每日一题.445:两数相加II
92 0
数位排序-蓝桥杯-2022
利用HashMap该数据结构的特点,因为1到n不会有重复,而且把它做为键
|
测试技术
【寒假每日一题】AcWing 4653. 数位排序(补)
目录 一、题目 1、原题链接 2、题目描述 二、解题报告 1、思路分析 2、时间复杂度 3、代码详解 三、知识风暴 关于pair
110 0