【CF 549G Happy Line】排序

题目链接:http://codeforces.com/problemset/problem/549/G

题意:给定一个n个元素的整数序列a[], 任意时刻对于任一对相邻元素a[i-1]、 a[i],若a[i-1] < a[i] 则要依次执行如下两个操作:

  1. a[i-1]--, a[i]++;

  2. 交换a[i-1]和a[i]的位置。

经过若干次1、2操作后,若能使整个序列变成非降的,则输出最终的序列;否则输出":("。

数据范围:n 属于 [1, 2*10^5], a[i] 属于[0, 10^9]

思路:首先想到交换排序,但n 在10^5所以n^2的排序不可取。后来模拟快排的过程推出了样例,如下:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #include <queue>
 5 #define CLEAR(A, X) memset(A, X, sizeof(A))
 6 #define REP(N) for(int i=0; i<(N); i++)
 7 #define REPE(N) for(int i=1; i<=(N); i++)
 8 #define FREAD(FN) freopen((FN), "r", stdin)
 9 #define pb(a) push_back(a)
10 #define pf() pop_front()
11 using namespace std;
12 
13 const int MAX_N = 200005;
14 int n;
15 int a[MAX_N];
16 int flag;
17 void partition(int s, int e){
18     if(s == e) return ;
19     int i = s, j = e - 1;
20     //printf("i %d j %d*******\n", i, j);
21     while(i < j){
22         while(i < j && a[j] >= a[i]) j--;
23         if(i < j){
24             a[j] += j - i;
25             if(a[i] == a[j]){
26                 flag = 1;
27                 return ;
28             }
29             a[i] -= j - i;
30             swap(a[i], a[j]);
31             i++;
32         }
33         
34         while(i < j && a[i] <= a[j]) i++;
35         if(i < j){
36             a[i] -= j - i; 
37             if(a[i] == a[j]){
38                 flag = 1;
39                 return ;
40             }
41             a[j] += j - i;
42             swap(a[i], a[j]);
43             j--;
44         }
45         // for(int k=0; k<n; k++) printf("%d ", a[k]);
46         // printf("\n");
47     }//i == j
48     partition(s, i);
49     partition(i+1, e);
50 }
51 
52 int main()
53 {
54     scanf("%d", &n);
55     REP(n) scanf("%d", &a[i]);
56     flag = 0;
57     partition(0, n);
58     if(flag) printf(":(\n");
59     else{
60         REP(n) printf("%d ", a[i]);
61         printf("\n");
62     }
63     return 0;
64 }
quickSort,i, j相对往中间走

但对于第六个test(

5
15 5 8 6 3

)得到的结果是错的,尝试改用i, j 指针同方向走来构造轴点,如下,但还是构造不出正确的结果。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #include <queue>
 5 #define CLEAR(A, X) memset(A, X, sizeof(A))
 6 #define REP(N) for(int i=0; i<(N); i++)
 7 #define REPE(N) for(int i=1; i<=(N); i++)
 8 #define FREAD(FN) freopen((FN), "r", stdin)
 9 #define pb(a) push_back(a)
10 #define pf() pop_front()
11 using namespace std;
12 
13 const int MAX_N = 200005;
14 int n;
15 int a[MAX_N];
16 int flag;
17 void partition(int s, int e){
18     if(s == e) return ;
19     int i = s, j = s + 1;
20     int cur = s;
21     //printf("i %d j %d*******\n", i, j);
22     while(i < e && j < e){
23         while(i < j && j < e && a[j] >= a[i]) j++;
24         if(i < j && j < e){
25             a[j] += j - i;
26             if(a[i] == a[j]){
27                 flag = 1;
28                 return ;
29             }
30             a[i] -= j - i;
31             swap(a[i], a[j]);
32             cur = j;
33             i = j + 1;
34         }
35         
36         while(j < i && i < e && a[i] >= a[j]) i++;
37         if(j < i && i < e){
38             a[j] -= i - j; 
39             if(a[i] == a[j]){
40                 flag = 1;
41                 return ;
42             }
43             a[i] += i - j;
44             swap(a[i], a[j]);
45             cur = j;
46             j = i + 1;
47         }
48         // for(int k=0; k<n; k++) printf("%d ", a[k]);
49         // printf("\n");
50     }//i == j
51     partition(s, cur);
52     partition(cur+1, e);
53 }
54 
55 int main()
56 {
57     scanf("%d", &n);
58     REP(n) scanf("%d", &a[i]);
59     flag = 0;
60     partition(0, n);
61     if(flag) printf(":(\n");
62     else{
63         REP(n) printf("%d ", a[i]);
64         printf("\n");
65     }
66     return 0;
67 }
quickSort,i, j从左往右走

于是看题解了,以下是题解的思路,思想仍是排序,(虽然tag上写了greedy,但我没想明白哪里用到了贪心策略):

由于swap(a[i-1], a[i])时,向左的a[i]在数值上"收益"了1,向右的a[i-1]在数值上"消耗"了1,现在把由交换产生的“收益/消耗”变化量从a[i]的原始数值中分离开来。

如左图,每一列对应一个位置 i ,其中黑色的“台阶”加上黄色的“塔”为原始的a[i]值,现在规定从左到右台阶的高度从n 均匀递减到 1, 记黄色的塔高 b[i] = 原始高度a[i] - 台阶高度(n - i)(i从0起始);这样每个a[i] 向左交换相当于上一个台阶,向右交换为下一个台阶,对应的塔高b[i]是不变的,如右图。所以我们只需计算出序列b[i]并把它排成非降序,然后再加上对应位置的台阶高度就是最终结果了。对于":("的情况,只需得到结果后扫描一遍检查是否确实非降序即可。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #include <queue>
 5 #define CLEAR(A, X) memset(A, X, sizeof(A))
 6 #define REP(N) for(int i=0; i<(N); i++)
 7 #define REPE(N) for(int i=1; i<=(N); i++)
 8 #define FREAD(FN) freopen((FN), "r", stdin)
 9 #define pb(a) push_back(a)
10 #define pf() pop_front()
11 using namespace std;
12 
13 const int MAX_N = 200005;
14 int n;
15 int a[MAX_N];
16 int flag;
17 
18 int main()
19 {
20     scanf("%d", &n);
21     REP(n) scanf("%d", &a[i]);
22     flag = 0;
23     REP(n) a[i] -= n - i;
24     sort(a, a+n);
25     a[0] += n;
26     for(int i=1; i<n; i++){
27         a[i] += n - i;
28         if(a[i] < a[i-1]){
29             flag = 1;
30             break;
31         }
32     }
33     
34     if(flag) printf(":(\n");
35     else{
36         REP(n) printf("%d ", a[i]);
37         printf("\n");
38     }
39     return 0;
40 }

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值