zoukankan      html  css  js  c++  java
  • [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 }
  • 相关阅读:
    重新整理 .net core 实践篇————配置系统之盟约[五]
    重新整理 .net core 实践篇————依赖注入应用之援军[四]
    重新整理 .net core 实践篇————依赖注入应用之生命法则[三]
    重新整理 .net core 实践篇————依赖注入应用[二]
    重新整理 .net core 实践篇————配置应用[一]
    spring cloud 学习笔记 客户端(本地)均衡负载(三)
    Leetcode之插入区间
    Leetcode之两棵二叉搜索树中的所有元素
    Leetcode之二叉树的层序遍历
    LeetCode之验证二叉搜索树
  • 原文地址:https://www.cnblogs.com/ac-evil/p/10351036.html
Copyright © 2011-2022 走看看