Valera and Swaps

题意:

定义 $f(p)$ 表示将 $p$ 序列变换为有序序列最少要交换多少次,给一 $1 sim n$ 的排列 $a$ ,给一整数 $m$,

求问将 $a$ 最少交换多少次能得到 $p$ ,使得 $f(p) = m$,并将以此交换视作一个两个数字,将交换按顺序排列开

求出字典序最小的交换序列。

解法:

记 $id$ 表示排列 $id(i) = i$

考虑 $f(p)$ 怎么求,可以发现,将原排列视作一个从$p$置换到$id$的置换,则将置换拆分成 $tot$ 个循环后,

最小交换次数就是$sum_{i=1}^{tot}{cnt_i - 1}$,也就是$n - tot$。

这样考虑交换两个置换的元素,两个置换群会合为一个置换群, $tot$ 变为 $tot-1$。

考虑交换一个置换群内的元素,当前置换群会拆分为两个置换群,$tot$ 变为 $tot+1$。

我们注意到要求交换次数最小,这样两种操作一定不会共存,

这样分类讨论:

1.$n-m < tot$时,我们需要将原先的置换群不断合并,要求字典序最小,

所以我们每次找出含最小元素的置换,将其与含1的置换合并。

2.$n-m = tot$时,不用交换,答案为0

3.$n-m > tot$时,每一次我们只要选择含最小元素的置换,将其中的最小值和次小值交换,并将置换拆开。

总复杂度$O(n^2)$

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <vector>
 5 
 6 #define N 3010
 7 #define LL long long
 8 
 9 using namespace std;
10 
11 int n,m,tot;
12 int a[N];
13 bool v[N];
14 vector<int> cir[N];
15 
16 int main()
17 {
18     while(~scanf("%d",&n))
19     {
20         for(int i=1;i<=n;i++) scanf("%d",&a[i]);
21         scanf("%d",&m);
22         m=n-m;
23         for(int i=1;i<=n;i++) v[i]=0;
24         tot=0;
25         for(int i=1;i<=n;i++)
26         {
27             if(v[i]) continue;
28             int tmp=i;
29             cir[++tot].clear();
30             while(!v[tmp])
31             {
32                 v[tmp]=1;
33                 cir[tot].push_back(tmp);
34                 tmp=a[tmp];
35             }
36         }
37         if(tot==m) cout<<0<<endl;
38         else if(tot>m)
39         {
40             cout<<tot-m<<endl;
41             for(int Te=1;Te<=tot-m;Te++)
42             {
43                 int t=0;
44                 for(int i=2;i<=tot;i++)
45                     if(!cir[i].empty() && (!t || cir[i][0]<cir[t][0]))
46                         t=i;
47                 cout<<cir[1][0]<<' '<<cir[t][0]<<' ';
48                 for(int i=0;i<(int)cir[t].size();i++)
49                     cir[1].push_back(cir[t][i]);
50                 cir[t].clear();
51             }
52         }
53         else
54         {
55             int tott=tot;
56             cout<<m-tot<<endl;
57             for(int Te=1;Te<=m-tot;Te++)
58             {
59                 int t=0;
60                 for(int i=1;i<=tott;i++)
61                     if(cir[i].size()>1 && (!t || cir[i][0]<cir[t][0]))
62                         t=i;
63                 int pos=1;
64                 for(int i=2;i<(int)cir[t].size();i++)
65                     if(cir[t][i]<cir[t][pos])
66                         pos=i;
67                 cout<<cir[t][0]<<' '<<cir[t][pos]<<' ';
68                 swap(cir[t][0],cir[t][pos]);
69                 cir[++tott].clear();
70                 int cnt=0;
71                 for(int i=pos;i<(int)cir[t].size();i++)
72                     cir[tott].push_back(cir[t][i]),cnt++;
73                 while(cnt--) cir[t].pop_back();
74             }
75         }
76     }
77     return 0;
78 } 
View Code
原文地址:https://www.cnblogs.com/lawyer/p/6575936.html