CF387D. George and Interesting Graph
题意:
给你一个有向图(不含重边),你可以加边删边,求最小操作次数后得到一个“有趣图”。
“有趣图”定义为:
- 存在一个中心结点 v v v,和其他每个结点都有双向边,且和自身有自环。
- 除了中心结点之外,其他结点的入度和出度都为 2 2 2。
思路:
一道很有意思的题目。
分析一下要求的图,我们可以发现等同于如下要求:
- 中心点有 2 ( n − 1 ) + 1 2(n-1)+1 2(n−1)+1条边,由于无重边,所以只能加边。
- 每个点除去和中心点的双向边,需要入度和出度都为 1 1 1。且这 n − 1 n-1 n−1个点会形成一个或多个(自)环。
我们首先算简单的一部分,对于第一点,统计需要和中心点
v
v
v连的边的个数
c
n
t
cnt
cnt。
接着考虑第二点,如果我们把一个点拆成一个入度点和一个出度点,那么对于一个入度点应该匹配一个出度点,就成了二分图匹配。我们可以得到二分图的边数为
q
q
q,如果要这
n
−
1
n-1
n−1对点两两匹配,就需要
n
−
1
n-1
n−1条边,我们假设得到的最大匹配数为
f
l
o
w
flow
flow,我们首先要删掉
q
−
f
l
o
w
q-flow
q−flow条多余的边,然后加上
n
−
1
−
f
l
o
w
n-1-flow
n−1−flow条需要的边。所以对于一个中心点
v
v
v,我们需要修改
c
n
t
+
q
−
f
l
o
w
+
n
−
1
−
f
l
o
w
cnt+q-flow+n-1-flow
cnt+q−flow+n−1−flow次。
枚举
v
v
v,取最小值。
代码:
#include<bits/stdc++.h>
#define pii pair<int,int>
#define ll long long
#define cl(x,y) memset(x,y,sizeof(x))
#define ct cerr<<"Time elapsed:"<<1.0*clock()/CLOCKS_PER_SEC<<"s.\n";
const int N=5e5+210;
const int M=1e3+10;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
const int minn=0xc0c0c0c0;
using namespace std;
struct edge
{
int u,v,w;
}maze[N<<1];
int len=1,head[M]={0};
int dep[M];//深度
void add(int u,int v,int w)
{
maze[++len]={head[u],v,w};
head[u]=len;
}
void inc(int u,int v,int w)
{
add(u,v,w);
add(v,u,0);
}
int dfs(int u,int f,int t)
{
int ans=0,i;
if(u==t)
return f;
for(i=head[u];i && f;i=maze[i].u)
{
int v=maze[i].v,w=maze[i].w;
if(dep[v]==dep[u]+1 && w)//符合深度关系且能流
{
int sum=dfs(v,min(f,w),t);
maze[i].w-=sum;
maze[i^1].w+=sum;
f-=sum;
ans+=sum;
}
}
if(!ans)
dep[u]=-2;
return ans;
}
int bfs(int s,int t)
{
queue<int> q;
cl(dep,0);
dep[s]=1;//源点深度为1
q.push(s);
while(!q.empty())
{
int u=q.front(),i;
q.pop();
for(i=head[u];i;i=maze[i].u)
{
int v=maze[i].v,w=maze[i].w;
if(w && !dep[v])//有深度且能流
{
dep[v]=dep[u]+1;
q.push(v);
}
}
}
return dep[t];
}
int dinic(int s,int t)
{
int ans=0;
while(bfs(s,t))
ans+=dfs(s,inf,t);
return ans;
}
int ma[M][M]={0},u[N],v[N];
signed main()
{
int n,m,i,j;
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++)
{
scanf("%d%d",&u[i],&v[i]);
ma[u[i]][v[i]]=1;
}
int s=0,t=2*n+1,ans=inf;
for(i=1;i<=n;i++)
{
cl(head,0);
len=1;
for(j=1;j<=n;j++)
if(j!=i)
inc(s,j,1);
for(j=1;j<=n;j++)
if(j!=i)
inc(j+n,t,1);
int cnt=0,q=0;
for(j=1;j<=m;j++)
if(u[j]!=i && v[j]!=i)
{
inc(u[j],v[j]+n,inf);
q++;
}
for(j=1;j<=n;j++)
{
if(!ma[i][j])
cnt++;
if(!ma[j][i])
cnt++;
}
if(!ma[i][i])
cnt--;
int fl=dinic(s,t);
ans=min(ans,q-fl+n-1-fl+cnt);
}
printf("%d\n",ans);
return 0;
}