BZOJ 1562 [NOI2009]变换序列:二分图匹配

时间:2022-12-16 15:13:19

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1562

题意:

  给定n,定义D(x,y) =  min(|x-y|, n-|x-y|),然后给定数组d[i] = D(i,T[i])。

  让你求一个0到n-1的排列T,下标i∈[0,n-1],满足给定的D(i,T[i]),且字典序最小。

  若没有答案输出"No Answer"。

 

题解:

  其实就是让你求一个排列T,其中T[i]要么填(i+d[i])%n,要么填(i-d[i]+n)%n。

  所以对于每个i,向它能填的两个数连边,跑一边最大匹配即可。

  又因为要求字典序最小,所以对于每个i连边时,应先连向较小的那个数。然后跑匈牙利的时候按从n-1到0的顺序跑即可。

 

AC Code:

 1 #include <iostream>
2 #include <stdio.h>
3 #include <string.h>
4 #include <vector>
5 #define MAX_N 10005
6
7 using namespace std;
8
9 int n;
10 int d[MAX_N];
11 int vis[MAX_N];
12 int mat[MAX_N];
13 int rmat[MAX_N];
14 int edge[MAX_N][2];
15
16 bool hungary(int now,int cnt)
17 {
18 for(int i=0;i<2;i++)
19 {
20 int temp=edge[now][i];
21 if(vis[temp]!=cnt)
22 {
23 vis[temp]=cnt;
24 if(mat[temp]==-1 || hungary(mat[temp],cnt))
25 {
26 mat[temp]=now;
27 rmat[now]=temp;
28 return true;
29 }
30 }
31 }
32 return false;
33 }
34
35 int main()
36 {
37 cin>>n;
38 for(int i=0;i<n;i++)
39 {
40 cin>>d[i];
41 if(d[i]>n/2)
42 {
43 cout<<"No Answer"<<endl;
44 return 0;
45 }
46 int x=(i+d[i])%n;
47 int y=(i-d[i]+n)%n;
48 edge[i][0]=min(x,y);
49 edge[i][1]=max(x,y);
50 }
51 memset(vis,0,sizeof(vis));
52 memset(mat,-1,sizeof(mat));
53 for(int i=n-1;i>=0;i--)
54 {
55 if(!hungary(i,i+1))
56 {
57 cout<<"No Answer"<<endl;
58 return 0;
59 }
60 }
61 for(int i=0;i<n;i++) cout<<rmat[i]<<" ";
62 cout<<endl;
63 }