序列重排

序列重排

给定一个长度为 n 的整数序列 a1,a2,,an

请你对序列进行重新排序(也可以保持原序列),要求新序列满足每个元素(第 1 个除外)都恰好是前一个元素的两倍或前一个元素的三分之一。

保证输入一定有解。

输入格式

第一行包含整数 n

第二行包含 n 个整数 a1,a2,,an

输出格式

一行 n 个整数,表示排序后的序列。输出任意合法方案即可。

数据范围

前三个测试点满足 2n10
所有测试点满足 2n1001ai3×1018

输入样例1:

6
4 8 6 3 12 9

输出样例1:

9 3 6 12 4 8 

输入样例2:

4
42 28 84 126

输出样例2:

126 42 84 28

输入样例3:

2
1000000000000000000 3000000000000000000

输出样例3:

3000000000000000000 1000000000000000000

 

解题思路

  首先这题保证有解。假设有满足题目要求的序列a1  a2  an。用xi来表示第i个数的因子2的数量,用yi来表示第i个数的因子3的数量。

  在这个序列中除了第1个数外,对于第i个数,要么aiai1的两倍,即xixi11;要么aiai113,即yiyi11。因此可以发现,对于任意一个数,它要么比前一个数的因子21,要么与前一个数的因子2数量相同。因此有xixi+1,即数列xi是递增的。

  如果某一个数与它前一个数的因子2的数量相同,即xi=xi1,由于这是一个合法的序列,因此必然有这个数的因子3数量比前一个数的少1,即yi=yi11

  因此我们可以得到一个必要条件,就是如果这是一个合法序列,那么必然有xi<xi+1或者xi=xi+1yi>yi+1。这是一个必要条件,也就是说如果是一个合法序列那么这个序列一定满足这个必要条件。而如果某个序列满足必要条件,那么这个序列不一定是一个合法序列。

  由于保证有解,因此对于任何一个序列重新排完序后,满足这个必要条件的序列是唯一的。下面来证明。

  首先不会有任何两个数aiaj的因子2数量和因子3的数量相同,即xi=xj, yi=yj。反证法,假设存在这个情况的话,首先i, j必然会是这个合法序列中的某两个位置,假设这两个数之间的数有ai  ak1  ak2  aj,因为这是一个合法序列,因此有xixk1xk2xj,由于xi=xj,因此中间所有数的因子2数量相等,即xi=xk1=xk2==xj。因为是合法序列,因子2的数量都相等,因此必然会有yi>yk1>yk2>>yj,这就与yi=yj矛盾了。因此结论是如果要使序列可以变成合法序列,那么一定不会存在两个数的因子2和因子3的数量分别相等。因此序列中任何两个数的xiyi不相等,根据双关键字排序,由于任何两个数都不会相等(即同时满足xi=xj, yi=yj),因此每个数都有严格的大小顺序,因此他们排完序后的结果是唯一的。

  所有,由于序列是有解的,因此不存在有两个数的因子2和因子3数量都分别相等,因此可以构造出一个满足必要条件的合法的序列,并且满足这个必要条件的序列是唯一的。

  假设合法的序列是S。我们根据双关键字排序后得到一个序列S1,这个序列满足必要条件,但不一定是合法的序列。又因为满足必要条件的序列是唯一的,因此S1就是S。这是因为S满足必要条件,S1也满足必要条件,因为一个序列满足必要条件不一定满足充分条件,但因为满足必要条件的序列是唯一的,因此满足必要条件的序列也会满足充分条件。

  AC代码如下:

复制代码
 1 #include <cstdio>
 2 #include <vector>
 3 #include <algorithm>
 4 using namespace std;
 5 
 6 typedef long long LL;
 7 
 8 const int N = 110;
 9 
10 vector<LL> a[N];
11 
12 int get(LL n, int b) {
13     int ret = 0;
14     while (n % b == 0) {
15         ret++;
16         n /= b;
17     }
18     return ret;
19 }
20 
21 int main() {
22     int n;
23     scanf("%d", &n);
24     for (int i = 0; i < n; i++) {
25         LL val;
26         scanf("%lld", &val);
27         a[i] = {get(val, 2), -get(val, 3), val}; // 因为第二关键字要升序,因此乘个负号
28     }
29     
30     sort(a, a + n);
31     
32     for (int i = 0; i < n; i++) {
33         printf("%lld ", a[i][2]);
34     }
35     
36     return 0;
37 }
复制代码

 

参考资料

  AcWing 4211. 序列重排(AcWing杯 - 周赛):https://www.acwing.com/video/3668/

posted @   onlyblues  阅读(298)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
历史上的今天:
2021-04-01 在堆区申请二维数组的方法
Web Analytics
点击右上角即可分享
微信分享提示