• hdu3861 强连通+最小路径覆盖


    题意:有 n 个点,m 条边的有向图,需要将这些点分成多个块,要求:如果两点之间有路径能够互相到达,那么这两个点必须分在同一块;在同一块内的任意两点相互之间至少要有一条路径到达,即 u 到达 v 或 v 到达 u;每个点都只能存在于单独一个块内。问最少需要划分多少块。

    首先,对于如果两点之间能够相互到达则必须在同一块,其实也就是在同一个强连通分量中的点必须在同一块中,所以首先就是强连通缩点。然后在同一块内的任意两点之间要有一条路,那么其实就是对于一块内的强连通分量,至少要有一条路径贯穿所有分量。而这一条路径上的所有强连通分量就可以构成同一块。那么其实我们就是需要找出最少的这样的路径将所有点全部覆盖一遍,就是做一遍最小路径覆盖。

    最小路径覆盖数=点数-拆点后的最大匹配数。

      1 #include<stdio.h>
      2 #include<string.h>
      3 #include<stack>
      4 #include<queue>
      5 using namespace std;
      6 
      7 const int maxn=10005;
      8 const int maxm=2e5+5;
      9 
     10 int head[2][maxn],point[2][maxm],nxt[2][maxm],size[2];
     11 int n,t,scccnt;
     12 int stx[maxn],low[maxn],scc[maxn];
     13 int vis[maxn],match[maxn];
     14 stack<int>S;
     15 
     16 void init(){
     17     memset(head,-1,sizeof(head));
     18     size[0]=size[1]=0;
     19 }
     20 
     21 void add(int a,int b,int c=0){
     22     point[c][size[c]]=b;
     23     nxt[c][size[c]]=head[c][a];
     24     head[c][a]=size[c]++;
     25 }
     26 
     27 void dfs(int s){
     28     stx[s]=low[s]=++t;
     29     S.push(s);
     30     for(int i=head[0][s];~i;i=nxt[0][i]){
     31         int j=point[0][i];
     32         if(!stx[j]){
     33             dfs(j);
     34             low[s]=min(low[s],low[j]);
     35         }
     36         else if(!scc[j]){
     37             low[s]=min(low[s],stx[j]);
     38         }
     39     }
     40     if(low[s]==stx[s]){
     41         scccnt++;
     42         while(1){
     43             int u=S.top();S.pop();
     44             scc[u]=scccnt;
     45             if(s==u)break;
     46         }
     47     }
     48 }
     49 
     50 void setscc(){
     51     memset(stx,0,sizeof(stx));
     52     memset(scc,0,sizeof(scc));
     53     t=scccnt=0;
     54     for(int i=1;i<=n;++i)if(!stx[i])dfs(i);
     55     for(int i=1;i<=n;++i){
     56         for(int j=head[0][i];~j;j=nxt[0][j]){
     57             int k=point[0][j];
     58             if(scc[i]!=scc[k]){    
     59                 add(scc[i],scc[k]+scccnt,1);
     60             }
     61         }
     62     }
     63 }
     64 
     65 int dfs1(int k){
     66     for(int i=head[1][k];~i;i=nxt[1][i]){
     67         if(!vis[point[1][i]]){
     68             int p=point[1][i];
     69             vis[p]=1;
     70             if(match[p]==-1||dfs1(match[p])){
     71                 match[p]=k;
     72                 return 1;
     73             }
     74         }
     75     }
     76     return 0;
     77 }
     78 
     79 
     80 int main(){
     81     int T;
     82     scanf("%d",&T);
     83     while(T--){
     84         int m;
     85         scanf("%d%d",&n,&m);
     86         init();
     87         while(m--){
     88             int a,b;
     89             scanf("%d%d",&a,&b);
     90             add(a,b);
     91         }
     92         setscc();
     93         int ans=0;
     94         memset(match,-1,sizeof(match));
     95         for(int i=1;i<=2*scccnt;++i){
     96             memset(vis,0,sizeof(vis));
     97             if(dfs1(i)==1)ans++;
     98         }
     99         printf("%d
    ",scccnt-ans);
    100     }
    101     return 0;
    102 }
    View Code
  • 相关阅读:
    移植BOA
    [转]Ubuntu(Linux)使用Eclipse搭建C/C++编译环境
    服务器软件设计的算法和问题
    [solve]Bind: Address Already in Use
    Simple guide for Automake/Autoconf by Nick
    Ubuntu(Linux)中设置samba
    VMware不能上网,解决办法
    数组的顺序存储和实现
    根文件系统的构成
    Linux网络编程IP转换函数inet_addr和inet_ntoa
  • 原文地址:https://www.cnblogs.com/cenariusxz/p/4811732.html
Copyright © 2020-2023  润新知