DAG(有向无环图)的最小链覆盖

DAG的最小链覆盖:用最少的链去覆盖一个DAG,求最少链数量(一个点也算一条链

最小链覆盖分为两种:链可以相交(可以重复经过某个点),链不可以相交(不可以重复经过某个点)(包括起点终点)

先讨论链不相交的情况,答案就是节点数减去二分图最大匹配数。
建图如下:
对于以下的图:

DAG(有向无环图)的最小链覆盖_第1张图片

如此建图即可:

DAG(有向无环图)的最小链覆盖_第2张图片

该图最大匹配和最小链覆盖答案均为2
跑一遍二分图匹配即可
证明分为感性和理性
感性:如果没有边,答案为n,每个点当一条链。
一条边联通两条链,会将两条链减少为一条链,答案减1
最小链覆盖要使答案减少的越多越好以保证链数最少
但是链不能相交(不能选重复顶点),所以减少的答案最多是二分图的最大匹配
所以最小链覆盖数目=总结点-二分图最大匹配
理性请见 https://blog.csdn.net/qq_34564984/article/details/52993626

如何处理链可以相交?

例如1->2->5,3->2->4,,可以相交答案为2(1->2->5,3->2->4),不能相交答案为3(1->2->5,3,4)。
发现一个问题:不用考虑链相交情况,1->2->5与1->5没有区别,它们一定联通。
考虑将可以联通的点直接连边,可以将图多连四条1->5,1->4,3->4,3->5的边,这样可以将问题转化(重要的数学思想)成链不相交问题,上面例子答案是2(转化成不相交时,会跑出1->5,3->4的答案,实际上就是1->2->5,3->2->4)
实现:运用floyd思想,枚举k、i、j,当满足(bz[i][k]==true&&bz[k][j]==true)时,bz[i][j]也赋成true(i、k联通,k、j联通,i、j必然联通)

问题至此解决

例题:(不相交)JZOJ1156 使命的召唤

#include 
#include 
using namespace std;
int i,j,k,m,n,o,p,l,t;
int x[100001],y[100001];
bool line[201][201],bz[201];
int done[100001];
void insert(int x,int y)
{
	line[x][y]=true;
}
int find(int x)
{
	for (int i=1;i<=n;i++)
	{
		if (line[x][i]==true&&bz[i]==false)
		{
			bz[i]=true;
			if (!done[i]||find(done[i]))
			{
				done[i]=x;
				return true;
			}
		}
	}
	return false;
}
int main()
{
	scanf("%d",&n);
	for (i=1;i<=n;i++) scanf("%d%d",&x[i],&y[i]);
	scanf("%d",&m);
	for (i=1;i<=m;i++) {
		scanf("%d%d",&o,&p);
		if (x[o]x[p]) line[p][o]=true;
	}
	o=0;
	for (i=1;i<=n;i++)
	{
		for (j=1;j<=n;j++) bz[j]=false;
		if (find(i)==true) o++;	
	}
	printf("%d\n",n-o);
}

(相交)JZOJ3423&&CODEVS2494 Vani和Cl2捉迷藏

几乎与bzoj1143祭祀完全相同

#include 
#include 
using namespace std;
int i,j,k,m,n,o,p,l,t;
int x[100001],y[100001];
bool f[201][201],bz[201];
int done[100001];
int read()
{
    int p=0,f=1;char c=getchar();
    while(c>='0'&&c<='9') p=p*10+c-'0',c=getchar();
    return f*p;
}
int find(int x)
{
	for (int i=1;i<=n;i++)
	{
		if (f[x][i]==true&&bz[i]==false)
		{
			bz[i]=true;
			if (!done[i]||find(done[i]))
			{
				done[i]=x;
				return true;
			}
		}
	}
	return false;
}
int main()
{
	freopen("Ribery.in","r",stdin);
	freopen("Ribery.out","w",stdout);
	n=read(),m=read();
	for (i=1;i<=m;i++) {
		o=read(),p=read(),f[o][p]=true;
	}
	for (k=1;k<=n;k++)
		for (i=1;i<=n;i++)
			if (f[i][k])
				for (j=1;j<=n;j++)
					if (f[k][j]) f[i][j]=true;
	o=0;
	for (i=1;i<=n;i++)
	{
		for (j=1;j<=n;j++) bz[j]=false;
		if (find(i)==true) o++;	
	}
	printf("%d\n",n-o);
	return 0;
}

你可能感兴趣的:(二分图匹配,图,最小链覆盖)