BZOJ 1562 [NOI2009]变换序列:二分图匹配
题目链接: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 }