hdu3861

/*
唉~~英语不好理解不了题意。我是网上先看别人做的题意,在做题。 唉~~~~~~悲哀~~~
题意:有个国王 要给自己的国家分块,然后给你一些路,两个城市可以互相到达的城市要在一个块(强连通分量),然后每个
城市只能在一个快,然后一个强连通到另一个强联通分量有变得可以划分为一个块,问你最少花费的块数,理解好题意就好做了。

思路:先用trajan算法缩点,然后重建图,重建图之后 就是一个最小路径覆盖了。
*/
#include<stdio.h>
#include<string.h>
#include<vector>
using namespace std;
int n,m;
vector<int>map[5001];
vector<int>map1[5001];
int dfn[5001],low[5001];//dfn表示点被访问的次序,low[i]表示i点能够追溯到本个强联通分量最前访问次序的那个点的访问次序。
int flag[5001];//标志素组
int stack[5001];//用来保存已经访问了得点
int color[5001];//染色
int match[5001];
int vis[5001];
int c;
int count=1,sign=0;
int min(int x,int y)
{
    if(x>y) return y;
    return x;
}
int tarjan(int u)
{   flag[u]=1;
    dfn[u]=low[u]=count++;
    stack[++c]=u;//赋初值
    for(int i=0;i<map[u].size();i++)
    {   int v=map[u][i];
        if(!flag[v])
           {
             tarjan(v);
            low[u]=min(low[u],low[v]);
           }
             else if(flag[v]==1)low[u]=min(low[u],low[v]);

    }
    if(dfn[u]==low[u])
    {
        sign++;
        do
        {   //printf("%d ",stack[c]);
            color[stack[c]]=sign;
            flag[stack[c]]=-1;

        }
        while(stack[c--]!=u&&c>=0);
    }
}
 void  inint()
 {
     for(int i=1;i<=n;i++)
     {
         map[i].clear();
         map1[i].clear();
     }

 }
 int find(int x)
 {

     for(int i=0;i<map1[x].size();i++)
     {
         int v=map1[x][i];
         if(!vis[v])
         {
             vis[v]=1;
            if(match[v]==-1||find(match[v]))
            {
                match[v]=x;
                return 1;
            }
         }
     }
     return 0;
 }
 int solve()
 {
     memset(match,-1,sizeof(match));
     int ans=0;
     for(int i=1;i<=sign;i++)
     {
         memset(vis,0,sizeof(vis));
         if(find(i)) ans++;
     }
     return ans;
 }
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    { scanf("%d%d",&n,&m);
      inint();
      for(int i=1;i<=m;i++)
      {
          int x,y;
          scanf("%d%d",&x,&y);
          map[x].push_back(y);
      }
      sign=0;c=0;
      memset(low,0,sizeof(low));
      memset(dfn,0,sizeof(dfn));
      memset(flag,0,sizeof(flag));
      memset(color,0,sizeof(color));
    for(int i=1;i<=n;i++)
        if(!flag[i])tarjan(i);
      for(int i=1;i<=n;i++)
     {
         for(int j=0;j<map[i].size();j++)
         {
             if(color[i]!=color[map[i][j]])
             {
                map1[color[i]].push_back(color[map[i][j]]);
             }
         }
     }

    printf("%d\n",sign-solve());

    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值