【二分图匹配】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 }

 

posted @ 2016-08-03 10:24  iiyiyi  阅读(152)  评论(0编辑  收藏  举报