HDU3861 The King’s Problem Tarjan缩点+二分图求最小路径覆盖

14 篇文章 0 订阅

这题一开始理解有问题。

对于每一个洲,如果洲里面的任意两个城市u,v,如果u有路径到v,则v也要有路径到u。不要求两两城市都存在路径。

用Tarjan求强连通分量缩点,在用二分图求


#ifdef _MSC_VER
#define DEBUG
#endif

#include <fstream>
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <string>
//#include <memory.h>
#include <limits.h>
#include <algorithm>
#include <math.h>
#include <numeric>
#include <functional>
#include <ctype.h>
#include <vector>
using namespace std;

#define  MAX 50005              //题目中可能的最大点数       
int STACK[MAX],top;          //Tarjan 算法中的栈 
bool InStack[MAX];             //检查是否在栈中 
int DFN[MAX];                  //深度优先搜索访问次序 
int Low[MAX];                  //能追溯到的在栈中的最早次序
int ComponentNumber;       //有向图强连通分量个数 
int Index;                 //索引号 
vector <int> Edge[MAX];        //邻接表表示 
int InComponent[MAX];			//记录每个点在第几号强连通分量里
void Tarjan(int i) 
{ 
  int j; 
  DFN[i]=Low[i]=Index++; 
  InStack[i]=true; 
  STACK[++top]=i; 
  for (size_t e=0;e<Edge[i].size();e++) 
  { 
    j=Edge[i][e]; 
    if (DFN[j]==-1) 
    { 
      Tarjan(j); 
      Low[i]=min(Low[i],Low[j]); 
    } 
    else if (InStack[j]) //如果指向的节点j仍在栈中,由于j先于i入栈,则j有到i的通路,同时由于i指向j,则i与j构成回路
      Low[i]=min(Low[i],DFN[j]); 	//如果指向的节点扔在栈中,则指向的节点仍未编入强连通分量
    //如果前面两个判断条件都是错误的话,则i和j不在同一个连通分量中
  } 
  if (DFN[i]==Low[i]) //连通分量中最早进栈的点
  { 
    ComponentNumber++; 
    do 
    { 
      j=STACK[top--]; 
      InStack[j]=false; 
//       Component[ComponentNumber].push_back(j);
      InComponent[j]=ComponentNumber;	//给每一个连通分量上的节点染色
    } 
    while (j!=i); 
  } 
}

void solve(int N)     //N是此图中点的个数,注意是0-indexed! 
{ 
  memset(STACK,-1,sizeof(STACK)); 
  memset(InStack,0,sizeof(InStack)); 
  memset(DFN,-1,sizeof(DFN)); 
  memset(Low,-1,sizeof(Low)); 
  top=0;Index=0;ComponentNumber=0;

  for(int i=1;i<=N;i++) 
    if(DFN[i]==-1) 
      Tarjan(i);    
}

vector<int> v_component_edge[MAX];
int used[MAX];
int mat[MAX];
bool find(const int &x)
{
  size_t i;
  for(i=0;i<v_component_edge[x].size();++i)
  {
    int v=v_component_edge[x][i];
    if(!used[v])
    {
      used[v]=1;
      if(mat[v]==-1 || find(mat[v]))
      {
        mat[v]=x;
        return true;
      }
    }
  }
  return false;
}

int main(void)
{
#ifdef DEBUG  
  freopen("../stdin.txt","r",stdin);
  freopen("../stdout.txt","w",stdout); 
#endif  

  int ncases,n,m,u,v;
  scanf("%d",&ncases);

  while(ncases--)
  {
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
    {
      Edge[i].clear();
      v_component_edge[i].clear();
    }
    while(m--)
    {
      scanf("%d%d",&u,&v);
      Edge[u].push_back(v);
    }

    solve(n);

    for(int i=1;i<=n;++i)
      for(size_t j=0;j<Edge[i].size();++j)
        if(InComponent[i]!=InComponent[Edge[i][j]])
          v_component_edge[InComponent[i]].push_back(InComponent[Edge[i][j]]);

    int ans=0;
    memset(mat,-1,sizeof(mat));
    for (int i=1;i<=ComponentNumber;++i)
    {
      memset(used,0,sizeof(used));
      if(find(i)) ++ans;
    }

    printf("%d\n",ComponentNumber-ans);

  }

  return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值