[BZOJ 1143][CTSC 2008]祭拜river(二分图最大独立集)

[BZOJ 1143][CTSC 2008]祭祀river(二分图最大独立集)

题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1143

这是我做的第一道CTSC的题,这题水得我都惊呆了。。。据说BZOJ只有第一问,没有问第二问,因为没数据,难怪这么水。。。

首先我们得知道二分图的独立集的概念:

二分图的独立集是二分图中一个任意两点都不相连的顶点的集合

二分图的最大独立集求法:

二分图的最大独立集=二分图点数-二分图最大匹配

然后这题就好做了。首先我们用Floyd求出相互可达的点的点对,然后用这些点对建立一个二分图,这个二分图中,有边相连的两个点都是相互可达的。然后我们求二分图的最大独立集即可。得到的二分图最大独立集中,任意两点之间均不可达。

注意边的个数要开n^2个!因为极限情况是任意两点之间均可达

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXV 110
#define MAXE 100100

using namespace std;

struct edge
{
    int u,v,next;
}edges[MAXE];

int head[MAXV],nCount=0;
int dist[MAXV][MAXV],n,m;
int linky[MAXV];
bool visit[MAXV];

void AddEdge(int U,int V)
{
    edges[++nCount].u=U;
    edges[nCount].v=V;
    edges[nCount].next=head[U];
    head[U]=nCount;
}

bool dfs(int u)
{
    for(int p=head[u];p!=-1;p=edges[p].next)
    {
        int v=edges[p].v;
        if(visit[v]) continue;
        visit[v]=true;
        if(linky[v]==-1||dfs(linky[v]))
        {
            linky[v]=u;
            return true;
        }
    }
    return false;
}

int main()
{
    memset(head,-1,sizeof(head));
    memset(linky,-1,sizeof(linky));
    scanf("%d%d",&n,&m);
    int ans=n;
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        dist[x][y]=1;
    }
    for(int k=1;k<=n;k++) //Floyd预处理,求出u能到达v的点对(u,v);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                dist[i][j]|=dist[i][k]&dist[k][j];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(i!=j)
                if(dist[i][j])
                    AddEdge(i,j);
    for(int i=1;i<=n;i++)
    {
        memset(visit,false,sizeof(visit));
        if(dfs(i)) ans--;
    }
    printf("%d\n",ans);
    return 0;
}