缩点+最小路径覆盖 hdu 3861

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3861

题意:输入t,表示t个样例。接下来每个样例第一行有两个数n,m表示点数和有向边的数量,接下来输入m条有向边,现在要我们把点划分成最少的块,每个块里面的点两两之间要至少有一条有向边可以从其中一个点到另一个点。

思路:分成的区域里面两个点之间至少要有一个点可以到达另一个点,并且要区域数最少,那么就是求最小路径覆盖,但是要求最小路径覆盖的前提是要是无环,所以我们要先用tarjan算法缩点,然后在在缩完点的图上面建一个二分图来求最小路径覆盖。

最小路径覆盖我是刚刚接触,推荐博客:https://www.cnblogs.com/jianglangcaijin/p/5989907.html

这里我用的是链式前向星,没有用vector,代码长了一点

代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
#include<stack>
#include<cmath>
#include<vector>
#include<set>
#include<cstdio>
#include<string>
#include<deque> 
using namespace std;
typedef long long LL;
#define eps 1e-8
#define INF 0x3f3f3f3f
#define maxn 5005
struct node{
    int v,next;
}edge1[maxn*2*10],edge2[maxn*2*10];
int head1[maxn],dfn[maxn],low[maxn],s[maxn],color[maxn];
int head2[maxn],pre[maxn*2];
bool vis[maxn*2];
int top,cnt1,time,color_num;
int cnt2,ans;
int n,m,k,t;
void init()
{
    memset(head1,-1,sizeof(head1));
    memset(dfn,0,sizeof(dfn));
    memset(vis,false,sizeof(vis));
    memset(pre,-1,sizeof(pre));
    memset(head2,-1,sizeof(head2));
    cnt1=time=color_num=top=0;
    cnt2=ans=0;
}
void addedge1(int u,int v)//第一次建图添加边 
{
    edge1[++cnt1].v=v;
    edge1[cnt1].next=head1[u];
    head1[u]=cnt1;
}
void addedge2(int u,int v)//第二次建图添加边 
{
    edge2[++cnt2].v=v;
    edge2[cnt2].next=head2[u];
    head2[u]=cnt2;
    
}
void DFS(int u)//缩点(染色) 
{
    low[u]=dfn[u]=++time;
    vis[u]=true;
    s[top++]=u;
    for(int i=head1[u];i!=-1;i=edge1[i].next)
    {
        int v=edge1[i].v;
        if(!dfn[v])
        {
            DFS(v);
            low[u]=min(low[u],low[v]);
        }
        else if(vis[v])
        low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        color_num++;
        int v;
        do{
            v=s[--top];
            vis[v]=false;
            color[v]=color_num;
        }while(u!=v);
    }
}
void tarjan()
{
    for(int i=1;i<=n;i++)
    {
        if(!dfn[i])
        DFS(i);
    }
}
void rebuild()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=head1[i];j!=-1;j=edge1[j].next)
        {
            int v=edge1[j].v;
            int a=color[i];
            int b=color[v];
            if(a!=b)
            addedge2(a,b);//建一个二分图
            //最小路径覆盖就是点数减最大匹配,注意前提是图里面无环,所以要先缩点 
        }
    }
}
bool hungry_DFS(int u)//求最大匹配 
{
    for(int i=head2[u];i!=-1;i=edge2[i].next)
    {
        int v=edge2[i].v;
        if(vis[v])
        continue;
        vis[v]=true;
        if(pre[v]==-1||hungry_DFS(pre[v]))
        {
            pre[v]=u;
            return true;
        }
    }
    return false;
}
void hungry()
{
    for(int i=1;i<=color_num;i++)
    {
        memset(vis,false,sizeof(vis));
        if(hungry_DFS(i))
        ans++;
    }
    printf("%d
",color_num-ans);//注意这里是缩点之后的点数减最大匹配数 
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        init();
        int u,v;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&u,&v);
            addedge1(u,v);//第一次建图 
        }
        tarjan();//缩点 
        rebuild();//第二次建图 
        hungry();//求最大匹配 
    }
    return 0;
}
原文地址:https://www.cnblogs.com/6262369sss/p/9806631.html