题目大意
给定长度为 n 的两行数,每次可以交换同一列上的两个数字,求最少的交换次数使得最后每一行数里都没有相同的数。保证有解。
Data Constraint
题解
一个显然的结论:同一列不会交换两次,同一个数不会出现超过两次。
把每一列视为一个点,接下来对于同一个数字X就有两种情况:
- X出现在同一行,那么这两列不能同时交换,也不能同时不交换。
- X出现在不同行,那么这两列必须同时交换,或者同时不交换。
这就是一个比较典型的2-SAT问题了。
直接跑一下,求个最小值即可。
SRC
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std ;
#define N 100000 + 10
bool Mark[N] ;
int Node[4*N] , Next[4*N] , Head[N] , tot ;
int wz[N] , S[N] ;
int n , top , ret , ans ;
void link( int u , int v ) {
Node[++tot] = v ;
Next[tot] = Head[u] ;
Head[u] = tot ;
}
void DFS( int x ) {
if ( Mark[x] ) return ;
Mark[x] = 1 ;
S[++top] = x ;
if ( x <= n ) ret ++ ;
for (int p = Head[x] ; p ; p = Next[p] ) DFS( Node[p] ) ;
}
int main() {
scanf( "%d" , &n ) ;
for (int i = 1 ; i <= n ; i ++ ) {
int x ;
scanf( "%d" , &x ) ;
if ( wz[x] ) {
link( wz[x] , i + n ) ;
link( i + n , wz[x] ) ;
link( wz[x] + n , i ) ;
link( i , wz[x] + n ) ;
} else wz[x] = i ;
}
for (int i = 1 ; i <= n ; i ++ ) {
int x ;
scanf( "%d" , &x ) ;
if ( wz[x] > 0 ) {
link( wz[x] , i ) ;
link( i , wz[x] ) ;
link( wz[x] + n , i + n ) ;
link( i + n , wz[x] + n ) ;
} else if ( wz[x] < 0 ) {
link( -wz[x] , i + n ) ;
link( i + n , -wz[x] ) ;
link( -wz[x] + n , i ) ;
link( i , -wz[x] + n ) ;
} else wz[x] = -i ;
}
for (int i = 1 ; i <= n ; i ++ ) {
if ( Mark[i] || Mark[i+n] ) continue ;
ret = top = 0 ;
DFS( i ) ;
int tmp = ret ;
while ( top ) Mark[S[top]] = 0 , top -- ;
ret = top = 0 ;
DFS( i + n ) ;
tmp = min( tmp , ret ) ;
ans += tmp ;
}
printf( "%d\n" , ans ) ;
return 0 ;
}
以上.