非常奈斯的一题
中心点非常特殊,而 n n n不大,考虑枚举中心点
那么去掉中心点后,每个点还需要一个出度和一个入度
而对于原图中的边自然是保留越多越好,这样删掉的边就少,加上的边就多
发现什么了??最大匹配!!!
这样一来,令最大匹配为 t e m p temp temp,图中的边有 b i a n bian bian条,点数为 n − 1 n-1 n−1
这部分花费 ( n − 1 − t e m p ) + ( b i a o − t e m p ) (n-1-temp)+(biao-temp) (n−1−temp)+(biao−temp)
再加上中心点需要补上去的边就好了
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
const int inf=1e9;
struct edge{
int to,nxt,flow;
}d[maxn]; int head[maxn],cnt=1;
int l[maxn],r[maxn],ok[maxn],vis[maxn],n,m,s,t;
int dis[maxn],gap[maxn];
void add(int u,int v,int flow)
{
d[++cnt]=(edge){v,head[u],flow},head[u]=cnt;
d[++cnt]=(edge){u,head[v],0},head[v]=cnt;
}
void bfs()
{
for(int i=0;i<=t;i++) dis[i]=-1,gap[i]=0;
gap[0]=1,dis[t]=0;
queue<int>q; q.push(t);
while( !q.empty() )
{
int u=q.front(); q.pop();
for(int i=head[u];i;i=d[i].nxt )
{
int v=d[i].to;
if( dis[v]==-1 )
{
dis[v]=dis[u]+1;
gap[dis[v]]++;
q.push(v);
}
}
}
}
int ISAP(int u,int flow)
{
if( u==t ) return flow;
int res=flow;
for(int i=head[u];i;i=d[i].nxt )
{
int v=d[i].to;
if( dis[v]+1==dis[u]&&d[i].flow )
{
int temp = ISAP(v,min(d[i].flow,res));
res-=temp;
d[i].flow-=temp,d[i^1].flow+=temp;
if( res==0 ) return flow;
}
}
if( --gap[dis[u]]==0 ) dis[s]=t+1;
gap[++dis[u]]++;
return flow-res;
}
int main()
{
cin >> n >> m;
s=0,t=n+n+1;
for(int i=1;i<=m;i++)
{
cin >> l[i] >> r[i];
if( l[i]==r[i] ) vis[l[i]]=1;
else ok[l[i]]++,ok[r[i]]++;//统计每个点的出点和入点
}
int res=1e9;
for(int i=1;i<=n;i++)
{
//枚举第i个点作为中心点
int ans=2*(n-1)-ok[i],bian=0;
if( !vis[i] ) ans++;//需要有自环
for(int j=1;j<=m;j++)
{
if( l[j]==i||r[j]==i ) continue;
add(l[j],r[j]+n,1);
bian++;
}
for(int j=1;j<=n;j++)
if( i!=j ) add(s,j,1),add(j+n,t,1);
bfs();
int temp=0;
while( dis[s]<t+1 ) temp+=ISAP(s,inf);
ans += (bian-temp)+(n-1-temp);
res = min( res,ans );
cnt=1;
for(int i=0;i<=t;i++) head[i]=0;
}
cout << res;
}