[NOI2009]变换序列

读懂题意后发现这道题最主要是要求出字典序最小的排列,考察了匈牙利算法的实质。

首先对于$D_i$的定义,我们可以解出可能的$T_i$,然后将$i$与$T_i$连边,求最大匹配。

如果最大匹配$<N$则说明$"No\ Answer"$。

但是要求字典序最小

第一中方法在我$AC$后翻看题解而写的。

这个程序是根据匈牙利算法的实质写的。

对于一个待匹配点,如果从其中一条非匹配边能匹配上,则这条匹配边一定会被匹配上

我们将所有$i$连向$T_i$的边从小到大排序,从最后一个点开始匹配,从小的$T_i$到大的$T_i$判断。最后答案一定是字典序最小的。

为什么呢?根据刚刚的总结,也就是能保证第$1$个点一定能选择最好的匹配,因为非匹配边从最好的往最坏的情况枚举,匹配上的一定就是最好的,因为假如前面没有匹配上,原因就在于选择前面的边一定不能匹配。

 1 #include <bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 #define re register
 6 #define rep(i, a, b) for (re int i = a; i <= b; ++i)
 7 #define repd(i, a, b) for (re int i = a; i >= b; --i)
 8 #define maxx(a, b) a = max(a, b);
 9 #define minn(a, b) a = min(a, b);
10 #define LL long long
11 #define inf (1 << 30)
12 
13 inline int read() {
14     int w = 0, f = 1; char c = getchar();
15     while (!isdigit(c)) f = c == '-' ? -1 : f, c = getchar();
16     while (isdigit(c)) w = (w << 3) + (w << 1) + (c ^ '0'), c = getchar();
17     return w * f;
18 }
19 
20 const int maxn = 1e4 + 5;
21 
22 int N, D[maxn], e[maxn][5], s[maxn], t[maxn];
23 
24 int lk[maxn], pt[maxn], vis[maxn];
25 
26 bool find(int u, int tag) {
27     rep(i, s[u], t[u]) {
28         int v = e[u][i];
29         if (vis[v] != tag) {
30             vis[v] = tag;
31             if (lk[v] == -1 || find(lk[v], tag)) {
32                 lk[v] = u;
33                 pt[u] = v;
34                 return true;
35             }
36         }
37     }
38     return false;
39 }
40 
41 int main() {
42     N = read();
43 
44     rep(i, 0, N-1) {
45         D[i] = read();
46         e[i][1] = D[i]+i, e[i][2] = i-D[i], e[i][3] = N+i-D[i], e[i][4] = D[i]-N+i;
47         sort(e[i]+1, e[i]+5);
48         s[i] = 1, t[i] = 4;
49         while (s[i] <= 4 && e[i][s[i]] < 0) s[i]++;
50         while (t[i] > 0 && e[i][t[i]] >= N) t[i]--;
51     }
52 
53     memset(vis, -1, sizeof(vis));
54     memset(lk, -1, sizeof(lk));
55     repd(i, N-1, 0)
56         if (!find(i, i)) {
57             printf("No Answer");
58             return 0;
59         }
60 
61     rep(i, 0, N-1)
62         printf("%d ", pt[i]);
63 
64     return 0;
65 }

第二种方法是我自己想到的,比上面的代码复杂。

首先有一点:由非匹配边和匹配边交替构成的回路(断句)翻转所有边的匹配与否(断句)得到的是另一种匹配。

所以我们先找出一个最大匹配,再通过上面的结论从第一个点调整使得解更优。最后同样也能得到字典序最小的匹配。

 1 #include <bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 #define re register
 6 #define rep(i, a, b) for (re int i = a; i <= b; ++i)
 7 #define repd(i, a, b) for (re int i = a; i >= b; --i)
 8 #define maxx(a, b) a = max(a, b);
 9 #define minn(a, b) a = min(a, b);
10 #define LL long long
11 #define inf (1 << 30)
12 
13 inline int read() {
14     int w = 0, f = 1; char c = getchar();
15     while (!isdigit(c)) f = c == '-' ? -1 : f, c = getchar();
16     while (isdigit(c)) w = (w << 3) + (w << 1) + (c ^ '0'), c = getchar();
17     return w * f;
18 }
19 
20 const int maxn = 1e4 + 5;
21 
22 int N, D[maxn], e[maxn][5], s[maxn], t[maxn];
23 
24 int lk[maxn], pt[maxn], vis[maxn];
25 
26 bool find(int u, int tag) {
27     rep(i, s[u], t[u]) {
28         int v = e[u][i];
29         if (vis[v] != tag) {
30             vis[v] = tag;
31             if (lk[v] == -1 || find(lk[v], tag)) {
32                 lk[v] = u;
33                 pt[u] = v;
34                 return true;
35             }
36         }
37     }
38     return false;
39 }
40 
41 bool dfs(int u, int rt) {
42     rep(i, s[u], t[u]) {
43         int v = e[u][i];
44         if (lk[v] < rt) continue;
45         if (v == pt[u]) {
46             if (u == rt) return false;
47             continue;
48         }
49         if (vis[v] != rt) {
50             vis[v] = rt;
51             if (lk[v] == rt || dfs(lk[v], rt)) {
52                 lk[v] = u;
53                 pt[u] = v;
54                 return true;
55             }
56         }
57     }
58     return false;
59 }
60 
61 int main() {
62     N = read();
63 
64     rep(i, 0, N-1) {
65         D[i] = read();
66         e[i][1] = D[i]+i, e[i][2] = i-D[i], e[i][3] = N+i-D[i], e[i][4] = D[i]-N+i;
67         sort(e[i]+1, e[i]+5);
68         s[i] = 1, t[i] = 4;
69         while (s[i] <= 4 && e[i][s[i]] < 0) s[i]++;
70         while (t[i] > 0 && e[i][t[i]] >= N) t[i]--;
71     }
72 
73     memset(vis, -1, sizeof(vis));
74     memset(lk, -1, sizeof(lk));
75     rep(i, 0, N-1)
76         if (!find(i, i)) {
77             printf("No Answer");
78             return 0;
79         }
80 
81     memset(vis, -1, sizeof(vis));
82     rep(i, 0, N-1)
83         dfs(i, i);
84 
85     rep(i, 0, N-1)
86         printf("%d ", pt[i]);
87 
88     return 0;
89 }

 

posted @ 2019-02-03 21:20  AC-Evil  阅读(214)  评论(0编辑  收藏  举报