P3430 [POI2005]DWU-Double-row (二分图染色+建图)
题意
2n个数站成两排(每个数在2n个数中最多出现两遍),一次操作可以交换任意一列中两个数,求使每行数不重复的最少操作数。
输入格式
第一行为一个整数n(1<=n<=50000),接下来的两行每行有n个数表示每行站着的n个士兵的身高(1<=士兵的身高<=100000)。 数据保证你能够合理地安排士兵的位置(即每个数在2n个数中最多出现两次)。
输出格式
只有一行,输出最小操作数。
样例
input
9
2 5 5 2 7 4 7 3 9
1 6 8 4 6 3 9 1 8
output
3
思路
对于同一行的相同数字,则需要上下交换,所以我们建一条关于列与列的边权为1的边,对于不同行的相同数字,则边权为1。然后在此基础上我们跑二分图染色,如果没有冲突(边权为0),相邻的点染同一个颜色,有冲突(边权为1)染不同的颜色。最后两种颜色计数,取少的即该连通块的答案。
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
//#pragma GCC optimize(3)
#define pb push_back
#define is insert
#define PII pair<int,int>
#define show(x) cerr<<#x<<" : "<<x<<endl;
//mt19937 mt19937random(std::chrono::system_clock::now().time_since_epoch().count());
//ll getRandom(ll l,ll r){return uniform_int_distribution<ll>(l,r)(mt19937random);}
const int INF=0x3f3f3f3f;//2147483647;
const int N=5e4+50,M=1e5+50;
const ll mod=998244353;
int n;
int a[N],b[N];
int tot=0;
struct node {
int to,nxt,val;
}e[N<<1];
int head[N];
void add_edge(int u,int v,int val){
e[tot].to=v,e[tot].nxt=head[u],e[tot].val=val,head[u]=tot++;
}
int mp[M];
int mpb[M];
bool vis[N];
int cnt[2];
void dfs(int x,int type,int pre){
//cout<<x<<endl;
vis[x]=1;cnt[type]++;
for(int i=head[x];~i;i=e[i].nxt){
int v=e[i].to;
if(v==pre||vis[v]){
continue;
}
if(e[i].val==1){
dfs(v,type^1,x);
}
else {
dfs(v,type,x);
}
}
}
void solve() {
memset(head,-1,sizeof head);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
if(mp[a[i]]){
add_edge(mp[a[i]],i,1);
add_edge(i,mp[a[i]],1);
}
mp[a[i]]=i;
}
for(int i=1;i<=n;i++){
cin>>b[i];
if(mp[b[i]]){
add_edge(mp[b[i]],i,0);
add_edge(i,mp[b[i]],0);
}
if(mpb[b[i]]){
add_edge(mpb[b[i]],i,1);
add_edge(i,mpb[b[i]],1);
}
mpb[b[i]]=i;
}
int ans=0;
for(int i=1;i<=n;i++){
if(!vis[i]){
cnt[0]=0,cnt[1]=0;
dfs(i,0,-1);
ans+=min(cnt[0],cnt[1]);
}
}
cout<<ans<<"\n";
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int __=1;//cin>>__;
while(__--){
solve();
}
return 0;
}