多校(HDU 7月12日 1009 Equivalent Sets 强连通分支 )

来源:互联网 发布:苹果cms精仿模板 编辑:程序博客网 时间:2024/05/16 01:30
 

 

Equivalent Sets

Time Limit: 12000/4000 MS (Java/Others)    Memory Limit: 104857/104857 K (Java/Others)
Total Submission(s): 550    Accepted Submission(s): 81


Problem Description
To prove two sets A and B are equivalent, we can first prove A is a subset of B, and then prove B is a subset of A, so finally we got that these two sets are equivalent.You are to prove N sets are equivalent, using the method above: in each step you can prove a set X is a subset of another set Y, and there are also some sets that are already proven to be subsets of some other sets.Now you want to know the minimum steps needed to get the problem proved.
 

Input
The input file contains multiple test cases, in each case, the first line contains two integers N <= 20000 and M <= 50000.Next M lines, each line contains two integers X, Y, means set X in a subset of set Y.
 

Output
For each case, output a single integer: the minimum steps needed.
 

Sample Input
4 03 21 21 3
 

Sample Output
42
Hint
Case 2: First prove set 2 is a subset of set 1 and then prove set 3 is a subset of set 1.
 

Source

HNU

 

我的图论还处在菜鸟阶段,当时比赛的时候看出了是强连通的题,但是tarjon算法还不会敲,于是在网上找的模板。。。我承认我可耻了。。。

找至少添几条边可以使有向图强连通,缩点之后记录下出度为零和入度为零的点的个数取最大的就可以了,当然还要特判下图本身是强连通的,我就因为这个WA了一次,不过也不在乎了,因为本身这题也不会么。。。 POJ类似题2186 popular cows 也是抄的模板的原题

AC的代码,应该是别人的代码:

#include<iostream> 
using namespace std; 
int dfn[20010],low[20010],times,C,s[20010],S[20010],top;    
bool in[20010]; 
struct LIST 

    int v;  LIST *next; 
}; 
 
LIST *head[20010],*rear[20010]; 
 
int chudu[20010],indu[20010]; 
 
void tarjan(int v)      /*tarjan求图的强连通分量*/ 

    dfn[v]=low[v]=++times; 
    S[top++]=v; 
    in[v]=true; 
    for(LIST *p=head[v];p!=NULL;p=p->next) 
        if(!dfn[p->v]) 
        { 
            tarjan(p->v); 
            if(low[p->v]<low[v]) 
                low[v]=low[p->v]; 
        } 
        else if(in[p->v]&&low[p->v]<low[v]) 
            low[v]=low[p->v]; 
 
    if(low[v]==dfn[v]) 
    { 
        C++; 
        do 
        { 
            v=S[--top]; 
            in[v]=false; 
            s[v]=C;             /*缩图,标记属于同一强连通分量的点*/ 
        }while(low[v]!=dfn[v]); 
    } 

 
 
int main() 

    int n,m,i,a,b; 
    while(cin>>n>>m) 
    { 
        memset(head,0,sizeof(int)*20010); 
        memset(rear,0,sizeof(int)*20010); 
        memset(s,0,sizeof(int)*20010); 
        top=0; 
        for(i=0;i<m;i++)         /*邻接表存储关系图*/ 
        { 
            scanf("%d%d",&a,&b); 
            if(rear[a]!=NULL) 
            { 
                rear[a]->next=new LIST; 
                rear[a]=rear[a]->next; 
            } 
            else 
                head[a]=rear[a]=new LIST; 
            rear[a]->next=NULL; 
            rear[a]->v=b; 
        } 
        times=0; C=0; 
        memset(dfn,0,sizeof(int)*20010);
        memset(low,0,sizeof(int)*20010);
        memset(in,0,sizeof(bool)); 
        for(i=1;i<=n;i++) 
            if(!dfn[i]) 
                tarjan(i); 
        memset(chudu,0,sizeof(chudu)); 
        memset(indu,0,sizeof(indu));
 
        for(i=1;i<=n;i++)                        /*计算缩图后每个点的出度*/ 
            for(LIST *p=head[i];p!=NULL;p=p->next) 
                if(s[i]!=s[p->v]) 
                    chudu[s[i]]=1,indu[s[p->v]]=1;
       
        a=b=0; 
        for(i=1;i<=C;i++) 
            if(!chudu[i]) 
                a++; 
        for (i=1 ; i<=C ; i++)
         if(!indu[i])b++;
         if(C==1)printf("0\n");
        //printf("%d %d\n",a,b);
        else
        printf("%d\n",a>b?a:b);
    } 
    return 0; 
 

HNU的解题报告 (依然看不懂)

题目大意:给出一个有向图,求最少添加多少条边使得该图变成强连通图。

首先如果图本身就是强连通那么答案为0。

否则先缩强连通分量将图变为DAG,然后算出入度为0的点和出度为0的点的个数,取最大值即为答案。

难度:★★

#include <stdio.h>
#include <string.h>

const int MAXN = 20001;
const long MAXM = 50001;

long head[2][MAXN], next[2][MAXM], edge[2][MAXM];
int n;
long m;

int a[MAXN], b[MAXN], c[MAXN];
int k;
long i, j, tot;

void search(const int vtx) {
 b[vtx] = 1;
 for (long i = head[0][vtx]; i; i = next[0][i])
  if (!b[edge[0][i]]) search(edge[0][i]);
 a[++tot] = vtx;
}

void _search(const int vtx) {
 b[vtx] = tot;
 for (long i = head[1][vtx]; i; i = next[1][i])
  if (!b[edge[1][i]]) _search(edge[1][i]);
}

int main() {
while (scanf("%d%ld", &n, &m) == 2) {
 memset(head, 0, sizeof(head));
 for (i = 1; i <= m; ++i) {
  scanf("%ld%d", &j, &k);
  edge[0][i] = k;
  next[0][i] = head[0][j];
  head[0][j] = i;

  edge[1][i] = j;
  next[1][i] = head[1][k];
  head[1][k] = i;
 }

 memset(b, 0, sizeof(b));
 for (tot = 0, i = 1; i <= n; ++i)
  if (!b[i]) search(i);
 memset(b, 0, sizeof(b));
 for (tot = 0, i = n; i; --i)
  if (!b[a[i]]) {
   ++tot;
   _search(a[i]);
  }

 if (tot <= 1) {
  puts("0");
  continue;
 }
 m = tot;
 memset(a, 0, sizeof(a));
 memset(c, 0, sizeof(c));
 for (tot = 0, i = 1; i <= n; ++i)
  for (k = b[i], j = head[0][i]; j; j = next[0][j])
   if (k != b[edge[0][j]]) {
    ++a[b[edge[0][j]]];
    ++c[k];
   }

 for (tot = n = 0, i = 1; i <= m; ++i) {
  if (!a[i]) ++tot;
  if (!c[i]) ++n;
 }
 printf("%ld\n", tot > n ? tot : n);
}
 return 0;
}