JZOJ4675. Double-row

题目大意

给定长度为 n 的两行数,每次可以交换同一列上的两个数字,求最少的交换次数使得最后每一行数里都没有相同的数。保证有解。

Data Constraint
n50000

题解

一个显然的结论:同一列不会交换两次,同一个数不会出现超过两次。
把每一列视为一个点,接下来对于同一个数字X就有两种情况:

  1. X出现在同一行,那么这两列不能同时交换,也不能同时不交换。
  2. 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 ;
}

以上.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值