hdu3861(tarjan缩点+最小路径覆盖)

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

题意:有n个城市,m条路(有方向的),国王想把n个城市划最少个数的州,便于管理。两两相连的城市一定划为一个州,在同一个州的任意两个城市相互之间至少要有一条路径到达,即u到达v或v到达u;每个点都只能在一个州里。问最少需要划分多少个州。

 

思路:先将n个城市用tarjan算出强联通分量,并将同一连通分量的城市归为一类,一个强连通分量就相当于一个点(可以看作一个城市)。再算出点与点是否相连(两个相连的点可以为一个州),有二分图匹配(匈牙利算法)算出最大匹配数,答案就是强联通分量数减去最大匹配数。

为什么答案是强联通分量数减去最大匹配数?

我们可以这样想,我们先把一个强联通分量的所有城市看作一个点(要缩点)(可看成一个城市),点与点之间相连就可以划分一个州,这是最后的情况,这样这个州的城市都至少有一条路这个到那个。问题是要最少的州,那么我们二分图匹配,算出可以匹配的点,剩下的不可以匹配的就是答案了,所以答案就是强联通分量数减去最大匹配数。

 

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define maxn 100050
using namespace std;
struct node{
    int v,nxt;
}edge[maxn],e[maxn];
int head[maxn],low[maxn],dfn[maxn],vis[maxn],stack[maxn];
int top,cnt,scs,num,ans;
int used[maxn],f[maxn],rt[maxn],x[maxn],y[maxn],newhead[maxn];

void init()//初始化 
{
    memset(vis,0,sizeof(vis));
    memset(head,-1,sizeof(head));
    memset(stack,0,sizeof(stack));
    memset(dfn,0,sizeof(dfn));
    memset(f,0,sizeof(f));
    memset(newhead,-1,sizeof(newhead));
    top=cnt=scs=num=0;
}

void add(int u,int v)//城市和城市相连 
{
    edge[cnt].v=v;
    edge[cnt].nxt=head[u];
    head[u]=cnt++;
}

void add1(int u,int v)//强联通分量相连 
{
    e[cnt].v=v;
    e[cnt].nxt=newhead[u];
    newhead[u]=cnt++;
}
void tarjan(int u)//tarjan算出强连通分量,并缩点(将同一强连通分量的城市看作一个点) 
{
    low[u]=dfn[u]=++scs;
    stack[top++]=u;
    vis[u]=1;
    for(int i=head[u];i!=-1;i=edge[i].nxt)
    {
        int v=edge[i].v;
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(vis[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        int t;
        num++;
        do
        {
            t=stack[--top];
            vis[t]=0;
            rt[t]=num;//缩点,强连通分量编号
        }while(t!=u);
    }
}

bool find(int x)//匈牙利算法中的是否可以匹配,让位置 
{
    for(int i=newhead[x];i!=-1;i=e[i].nxt)
    {
        int v=e[i].v;
        if(!used[v])
        {
            used[v]=1;
            if(f[v]==0||find(f[v]))
            {
                f[v]=x;
                return true;
            }
        }
    }
    return false;
}

int main()
{
    int t,n,m,ans;
    cin>>t;
    while(t--)
    {
        init();
        cin>>n>>m;
        for(int i=0;i<m;i++)
        {
            cin>>x[i]>>y[i];
            add(x[i],y[i]);
        }
        for(int i=1;i<=n;i++)
            if(!dfn[i])
                tarjan(i);
        cnt=0;
        for(int i=0;i<m;i++)//强连通分量相连 
        {
            int xx=rt[x[i]],yy=rt[y[i]];
            if(xx!=yy)//强连通分量不同 ,由于城市相连,所以 强连通分量相连可能划为一个州
                add1(xx,yy);    
        }
        ans=0;
        for(int i=1;i<=num;i++)//二分图最大匹配 
        {
            memset(used,0,sizeof(used));
            if(find(i))
                ans++;//最大匹配数 
        }
        //cout<<num<<" "<<ans<<endl;
        cout<<num-ans<<endl;
    }
    return 0;
}
原文地址:https://www.cnblogs.com/xiongtao/p/9385733.html