您的位置:首页 > 理论基础 > 计算机网络

bzoj1143 祭祀river 【网络流&&最长反链】

2018-01-23 10:37 232 查看

解题思路:

反链的定义就是一个点集满足其内部点两两不连通。

有定理如下:最长反链长度=最小可相交路径覆盖。

对偶定理:最长路径长度=最小反链覆盖。

先考虑有向无环图最小不相交路径覆盖

把原图中的每个点V拆成Vx和Vy,如果有一条有向边A->B,那么就加边Ax-By。这样就得到了一个二分图,最小路径覆盖=原图的节点数-新图最大匹配。

简单证明:一开始每个点都独立的为一条路径,总共有n条不相交路径。我们每次在二分图里加一条边就相当于把两条路径合成了一条路径,因为路径之间不能有公共点,所以加的边之间也不能有公共点,这就是匹配的定义。所以有:最小路径覆盖=原图的节点数-新图最大匹配。

有向无环图最小可相交路径覆盖

先floyd求出连通性后,把联通性作为边,最小不相交路径覆盖。

也可以每个Vy向Vx连INF的边,再正常连边求最小不相交路径覆盖(推荐)。

这道题既可以看作是求最长反链长度,也可以看作求连通性意义下的最大独立集。

关于更多二分图应用可见这里

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int getint()
{
int i=0,f=1;char c;
for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());
if(c=='-')f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}

const int N=205,M=10005,INF=0x3f3f3f3f;
int n,m,S,T;
int tot=1,first
,cur
,dis
,to[M],cap[M],nxt[M];
queue<int>q;

void add(int x,int y,int z)
{
nxt[++tot]=first[x],first[x]=tot,to[tot]=y,cap[tot]=z;
nxt[++tot]=first[y],first[y]=tot,to[tot]=x,cap[tot]=0;
}

bool BFS()
{
for(int i=S;i<=T;i++)cur[i]=first[i],dis[i]=-1;
while(!q.empty())q.pop();
dis[S]=0,q.push(S);
while(!q.empty())
{
int u=q.front();q.pop();
for(int e=first[u];e;e=nxt[e])
{
int v=to[e];
if(dis[v]==-1&&cap[e])
{
dis[v]=dis[u]+1;
q.push(v);
if(v==T)return true;
}
}
}
return false;
}

int dinic(int u,int flow)
{
if(u==T)return flow;
int res=0;
for(int &e=cur[u];e;e=nxt[e])
{
int v=to[e];
if(dis[v]==dis[u]+1&&cap[e])
{
int det=dinic(v,min(flow-res,cap[e]));
cap[e]-=det,cap[e^1]+=det;
res+=det;if(res==flow)break;
}
}
if(res<flow)dis[u]=-1;
return res;
}

int maxflow()
{
int res=0;
while(BFS())res+=dinic(S,INF);
return res;
}

int main()
{
//freopen("lx.in","r",stdin);
n=getint(),m=getint();
S=0,T=2*n+1;
for(int i=1;i<=n;i++)add(S,i,1),add(i+n,T,1),add(i+n,i,INF);
while(m--)
{
int x=getint(),y=getint();
add(x,y+n,1);
}
printf("%d\n",n-maxflow());
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: