POJ 2594 可相交的最小路径覆盖

//一个PXP的有向图中,路径覆盖就是在图中找一些路径,使之覆盖了图中的所有顶点,且任何一个顶点有且只有一条路径与之关联;
//(如果把这些路径中的每条路径从它的起始点走到它的终点,
//那么恰好可以经过图中的每个顶点一次且仅一次);如果不考虑图中存在回路,那么每条路径就是一个弱连通子集.
//
//果然正常的最小路径覆盖就是不相交的,每个点最多和2个点匹配,不会出现相交路径。
//最小路径覆盖
//
//题意:不是赤裸裸的最小路径覆盖(走遍所有的点),正常的最小路径覆盖中两个人走的路径不能有重复的点,而本题可以重复。
//
//分析:我们仍可将问题转化为最小路径覆盖。如果一个人需要经过另一个人走过的点的时候,让他直接从该点上空飞过去,
//越过该点,直接走下一个点。如果我们赋予每个人这种能力,那么求得的无重复点的最小路径覆盖结果,就是题目要求的结果,
//因为需要重复的地方只要飞过去,
//就可以不重复了。赋予这个能力的方法就是把所有点能间接到达的点全都改为直接到达。然后正常求最小路径覆盖即可。
//
//
//我觉得应该画个图就明白了。。。
//只是把本该连k点的点连到m点上了,增大了匹配数,但实际意义是不变的。
//共m个妹子,n个呆瓜,要尽量多的给每个呆瓜找个妹子
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<string>
#define maxn 600
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f
using namespace std;


bool link[maxn][maxn];
int g[maxn];
int graph[maxn][maxn];
int n;
bool use[maxn];

bool find(int x)
{
	for(int j=1;j<=n;j++)
	{		if(link[x][j]&&use[j]==false)//他们之间有好感且试图改变过这个妹子的归属问题没成功(?)
		{
			use[j]=true;
			if(g[j]==-1||find(g[j]))//名花无主或者名花能腾出个位置来,就是她的原配换个妹子
			{
				g[j]=x;
				return true;
			}
		}
	}
	return false;
}

int hungary()
{
	int match=0;
	for(int i=1;i<=n;i++)
	{
		memset(use,false,sizeof(use));
		if(find(i))
		match++;
	}
	return match;
}
///500个点跑floyd应该不会超时吧。。

void floyd()///用continue,不用break
{
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
        {
        if(graph[i][k]>=inf) continue;
        for(int j=1;j<=n;j++)
        {
        if(graph[k][j]>=inf) continue;
        graph[i][j]=min(graph[i][j],graph[i][k]+graph[k][j]);
        }
        }
        //cout<<graph[1][3]<<" 1 3"<<graph[3][1]<<endl;
        //cout<<graph[1][5]<<" 1 5 "<<graph[5][1]<<endl;
}

///可间接连边的也变为可连接的。
int main()
{
int m;
while(scanf("%d%d",&n,&m)!=EOF)
{
    if(n==0 && m==0) break;
    memset(link,false,sizeof(link));
    memset(g,-1,sizeof(g));
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        graph[i][j]=inf;
    for(int i=1;i<=n;i++)
        graph[i][i]=0;
        int tempa,tempb;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&tempa,&tempb);
        graph[tempa][tempb]=1;
    }
   // cout<<graph[5][1]<<"  1,5  "<<graph[1][5]<<endl;
    floyd();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if((graph[i][j]<inf)&&(i!=j))
            {
             link[i][j]=true;
             //cout<<"true"<<i<<" "<<j<<endl;
            }
       cout<<(n-hungary())<<endl;
   // cout<<n-hungary()<<endl;
}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值