P3430 [POI2005]DWU-Double-row (二分圖染色+建圖)
阿新 • • 發佈:2022-03-30
題意
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; }