• 【二分图匹配】BZOJ1562-[NOI2009] 变换序列


    【题目大意】

    对于0,1,…,N-1的N个整数,给定一个距离序列D0,D1,…,DN-1,定义一个变换序列T0,T1,…,TN-1使得每个i,Ti的环上距离等于Di。一个合法的变换序列应是0,1,…,N-1的一个排列,任务是要求出字典序最小的那个变换序列。(概括by:BYVoid)

    【思路】

    我们意识流现象一下。平时二分图匹配我们会根据从前往后,后面的会占用前方匹配,使得前方节点需要重新匹配。所以得出结论——后面的点会连到比较小的。那么我们就可以yy出这样一个做法:把每一个点连的边从小到大排序,从后往前进行匈牙利算法。

    一个剪枝:有一些节点存在唯一的匹配,可以直接预处理好这些匹配唯一的点,余下的点再跑匈牙利算法。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #include<vector>
     6 using namespace std;
     7 const int MAXN=10000+500;
     8 vector<int> E[MAXN],rE[MAXN];
     9 int n,in[MAXN],visfr[MAXN],visto[MAXN],vis[MAXN],lk[MAXN],ans[MAXN],cnt=0;
    10 
    11 void addedge(int u,int v)
    12 {
    13     E[u].push_back(v);
    14     rE[v].push_back(u); 
    15 }
    16 
    17 int find(int u)
    18 {
    19     for (int j=0;j<E[u].size();j++)
    20     {
    21         int i=E[u][j];
    22         if (!vis[i] && !visto[i])
    23         {
    24             vis[i]=1;
    25             if (!lk[i] || find(lk[i]))
    26             {
    27                 lk[i]=u;
    28                 ans[u]=i;
    29                 return 1;
    30             }
    31         }
    32     }
    33     return 0;
    34 }
    35 
    36 void init()
    37 {
    38     memset(visfr,0,sizeof(visfr));
    39     memset(visto,0,sizeof(visto));
    40     memset(in,0,sizeof(in));
    41     memset(lk,0,sizeof(lk));
    42     scanf("%d",&n);
    43     for (int i=1;i<=n;i++)
    44     {
    45         int d;
    46         scanf("%d",&d);
    47         int a=i+d;if (a>n) a-=n;
    48         int b=i-d;if (b<1) b+=n;
    49         if (a<b) addedge(i,a),addedge(i,b);
    50             else if (a>b) addedge(i,b),addedge(i,a);
    51                 else if (a==b)
    52                 {
    53                     visfr[i]=visto[a]=1;
    54                     ans[i]=a;
    55                     cnt++;
    56                 }
    57         if (a!=b) in[a]++,in[b]++;else in[a]++;
    58     }
    59     for (int i=1;i<=n;i++)
    60         if (in[i]==1 && !visto[i])
    61         {
    62             visto[i]=visfr[rE[i][0]]=1;
    63             ans[rE[i][0]]=i;
    64             cnt++;
    65         }
    66 }
    67 
    68 void solve()
    69 {
    70     for (int i=n;i>=1;i--)
    71     {
    72         if (visfr[i]) continue;
    73         memset(vis,0,sizeof(vis));
    74         if (find(i)) cnt++;else break;
    75     }
    76     if (cnt==n)
    77     {
    78         for (int i=1;i<=n;i++) 
    79         {
    80             printf("%d",(ans[i]+n-1)%n);
    81             if (i!=n) printf(" ");
    82         }
    83     }
    84     else puts("No Answer");
    85 }
    86 
    87 int main()
    88 {
    89     freopen("transform.in","r",stdin);
    90     freopen("transform.out","w",stdout);
    91     init();
    92     solve();
    93     return 0;
    94 }
  • 相关阅读:
    原生js实现购物车相关功能
    js+css让背景图片动起来
    彻底搞清楚rgba与opacity/filter的区别
    国家对五险一金的详细缴纳说明
    原生js+css实现二级伸缩菜单
    原生js实现table表格的各行变色功能
    原生js实现二级导航功能
    app下载文件,保存文件,展示文件(以图片文件为例)
    实现锚点跳转的两种方式及注意事项
    vue刷新页面及注意事项
  • 原文地址:https://www.cnblogs.com/iiyiyi/p/5731811.html
Copyright © 2020-2023  润新知