題意:有向圖500個點,1000條邊,刪除和添加一些邊使得除了一個點以外每個點的入度和出度爲2,剩下那一個點跟所有點都有連邊,問最小操作數。
思路:枚舉中心點,然後將剩下的點拆成2個點,入度和出度(精神分裂)。然後二分圖跑最大匹配,這樣就好搞了。
代碼:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define forn(i,n) for(int i=0;i<n;i++)
#define for1(i,n) for(int i=1;i<=n;i++)
#define IO ios::sync_with_stdio(false);cin.tie(0)
const int maxn = 505;
const int inf = 2e9+5;
int head[maxn],mat[maxn],n,m,tot;
bool vis[maxn];
struct edge{
int v,nex;
}e[maxn<<1];
void add(int u,int v){
e[++tot] = {v,head[u]},head[u] = tot;
}
vector<int>g[maxn];
bool find(int u){
for(auto &v:g[u])if(!vis[v]){
vis[v] = 1;
if(!mat[v]||find(mat[v])) {
mat[v] = u;
return 1;
}
}
return 0;
}
int main(){
IO;cin>>n>>m;
forn(i,m){
int u,v;cin>>u>>v;
add(u,v);
}
int ans = inf;
for1(cen,n){
for1(i,maxn-5) g[i].clear(),mat[i] = 0;
int in = 0,out = 0,cnt = 0,res = 0;
bool ok = 0;
for1(u,n){
if(u==cen){
for(int i = head[u];i;i = e[i].nex){
int v = e[i].v;
out++;
if(v==u) ok = 1;
}
}else{
for(int i = head[u];i;i = e[i].nex){
int v = e[i].v;
if(v==cen){
in++;
continue;
}
g[u].push_back(v);
}
}
}
if(ok) out--;
res+=(n-1)*2-in-out;
for1(i,n)if(!g[i].empty()){
for1(i,maxn-5) vis[i] = 0;
if(find(i)) cnt++;
}
if(ok) out++;
else res++;
res += m-in-out-cnt;
res += n-1-cnt;
ans = min(ans,res);
}
cout << ans <<'\n';
return 0;
}