poj Network of Schools (有向圖的強聯通分量)

題目大意

    有N個學校,這些學校之間用一些單向邊連接,若學校A連接到學校B(B不一定連接到A),那麼給學校A發一套軟件,則學校B也可以獲得。現給出學校之間的連接關係,求出至少給幾個學校分發軟件,才能使得所有的學校均可以獲得軟件;以及,至少需要添加幾條單向邊連接學校,才能使得給這些學校中任何一所發軟件,其餘的學校均可以收到。

第一是需要給多少個點,才能傳遍所有點。
第二問是加多少條邊,使得整個圖變得強連通。
使用Tarjan進行縮點,得到一個SCC圖、
這個圖有多少個入度爲0的,多少個出度爲0的。
假設有n個入度爲0,m個出度爲0

那麼第一個答案就是n,第二個答案是max(n,m)

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;

const int MAXN = 110;
const int MAXM = 110*110;

struct Edge
{
    int to,next;
}edge[MAXM];
int head[MAXN],tot;
int Low[MAXN],DFN[MAXN],Stack[MAXN],Belong[MAXN];
int Index,top;
int scc;//強聯通數 
bool Instack[MAXN];

void addedge(int u,int v)
{
    edge[tot].to = v;edge[tot].next = head[u];head[u] = tot++;
}
void Tarjan(int u)
{
    int v;
    Low[u] = DFN[u] = ++Index;
    Stack[top++] = u;
    Instack[u] = true;
    for(int i = head[u];i != -1;i = edge[i].next)
    {
        v = edge[i].to;
        if(!DFN[v]) // 如果新搜索到的節點是從未被搜索過  
        {
            Tarjan(v); // 那自然就得搜索這個節點  
            if(Low[u] > Low[v])// 回溯的時候改變當前節點的low值   
                  Low[u] = Low[v];
        }
        else if(Instack[v] && Low[u] > DFN[v])// 如果新搜索到的節點已經被搜索過而且現在在棧中 ,更新當前節點的low值,這裏的意思是兩個節點之間有一條可達邊,而前面 節點已經在棧中,那麼後面的節點就可能和前面的節點在一個聯通分量中     
            Low[u] = DFN[v];
    }
    if(Low[u] == DFN[u])
    {
        scc++;
        do
        {
            v = Stack[--top];// 彈出棧元素   
            Belong[v] = scc;// 爲了方便計算,將強聯通分量進行標記  
            Instack[v] = false; // 將棧內標記置爲0   
        }
        while( v!= u);  // 一直彈到u出現爲止   
    }
}
int in[MAXN],out[MAXN];
void solve(int N)
{
    memset(DFN,0,sizeof(DFN));
    memset(Instack,false,sizeof(Instack));
    Index = scc = top = 0;
    for(int i = 1;i <= N;i++)
        if(!DFN[i])// 如果某點沒被訪問過,則對其進行tarjan   
           Tarjan(i);   // tarjan的成果是得到了一個belong數組,記錄每個節點分別屬於哪個強聯通分量   
   
   
   
    if(scc == 1)
    {
        printf("1\n0\n");
        return;
    }
    for(int i = 1;i <= scc;i++)
       in[i] = out[i] = 0;
    //將強連通分支縮成點,然後統計每個強連通分支的入度和出度
	for(int u = 1;u <= N;u++)
    {
        for(int i = head[u];i != -1;i = edge[i].next)
        {
            int v = edge[i].to;
            if(Belong[u] != Belong[v]) //注意!!!自身不能連接到自身!
            {
                in[Belong[v]]++;
                out[Belong[u]]++;
            }
        }
    }
    int ans1=0,ans2=0;
    for(int i = 1;i <= scc;i++)
    {
        if(in[i]==0)ans1++;
        if(out[i]==0)ans2++;
    }
    printf("%d\n%d\n",ans1,max(ans1,ans2));

}
void init()
{
    tot = 0;
    memset(head,-1,sizeof(head));
}
int main()
{
    int n;
    int v;
    while(scanf("%d",&n) == 1)
    {
        init();
        for(int i = 1;i <= n;i++)
        {
            while(scanf("%d",&v)==1 && v)
            {
                addedge(i,v);
            }
        }
        solve(n);
    }
    return 0;
}

//#define LOCAL
#include <stdio.h>
#include <string.h>
#include <queue>
using namespace std;

#define MAXN 110
#define INF 0x3f3f3f3f
int n;
int map[MAXN][MAXN];
int low[MAXN];
int dfn[MAXN];
int stack[MAXN], head;
int instack[MAXN];
int belong[MAXN];
int in[MAXN];
int out[MAXN];
int index, cnt;

int min(int a, int b)
{
	return a < b ? a : b;
}

int max(int a, int b)
{
	return a > b ? a : b;
}

void init()
{
	int i, j;
	int temp;
	memset(map, 0, sizeof(map));
	memset(dfn, -1, sizeof(dfn));
	memset(low, 0, sizeof(low));
	memset(instack, 0, sizeof(instack));
	index = cnt = 1;
	head = 0;
	for(i= 1; i <= n; i++)
	{
		while(scanf("%d", &temp) && temp)
		{
			map[i][temp] = 1;
		}
	}
} 

void tarjan(int x)
{
	int i, a;
	low[x] = dfn[x] = index; // 剛搜到一個節點時low = dfn
	index++;
	stack[++head] = x; // 將該節點入棧 
	instack[x] = 1; // 將入棧標記設置爲1
	for(i = 1; i <= n; i++) // 遍歷入棧節點的邊
	{
		if(!map[x][i]) // 如果兩點之間沒有邊
			continue; // 不用管它
		if(dfn[i] == -1) // 如果新搜索到的節點是從未被搜索過
		{
			tarjan(i); // 那自然就得搜索這個節點
			low[x] = min(low[x], low[i]); // 回溯的時候改變當前節點的low值 
		}
		else if(instack[i]) // 如果新搜索到的節點已經被搜索過而且現在在棧中 
		{
			low[x] = min(low[x], dfn[i]); // 更新當前節點的low值,這裏的意思是兩個節點之間有一條可達邊,而前面 
		} 								  // 而前面節點已經在棧中,那麼後面的節點就可能和前面的節點在一個聯通分量中 
	} 
	
	if(low[x] == dfn[x]) // 最終退回來的時候 low == dfn , 沒有節點能將根節點更新,那 
	{					// low == dfn 的節點必然就是根節點
		int temp; 
		while(1) // 一直出棧到此節點, 這些元素是一個強聯通分量 
		{
			temp = stack[head--]; // 彈出棧元素 
			belong[temp] = cnt; // 爲了方便計算,將強聯通分量進行標記
		 	instack[temp] = 0; // 將棧內標記置爲0 
		 	if(temp == x)     // 一直彈到x出現爲止 
		 		break;
		}
		cnt++;
	} 
}						
 

void solve()
{
	int i, j;
	int t1, t2;
	while(scanf("%d", &n) != EOF) //
	{
		init(); // 初始化 
		for(i = 1; i <= n; i++) //  
			if(dfn[i] == -1) // 如果某點沒被訪問過,則對其進行tarjan 
				tarjan(i);			// tarjan的成果是得到了一個belong數組,記錄每個節點分別屬於哪個強聯通分量 
		
		for(i = 1; i <= n; i++) // 遍歷每條邊,找到縮點之後的邊 
		{
			for(j = 1;j <= n; j++)
			{
				if(map[i][j] && belong[i] != belong[j]) // 兩點之間有邊,但不是屬於一個強聯通分量的邊
				{
					out[belong[i]]++; // 縮點後的點入度+1 
					in[belong[j]]++;// 縮點後的點出度+1 
				} 
			}		
		}
		
		t1 = 0, t2 = 0;
		
		for(i = 1; i < cnt; i++)
		{
			if(in[i] == 0)
				t1++;
			if(out[i] == 0)
				t2++;
		}
		if(cnt == 2)
			printf("1\n0\n");
		else
			printf("%d\n%d\n", t1, max(t1, t2));
	}
}

int main()
{
#ifdef LOCAL
	freopen("poj1236.txt", "r", stdin);
	// freopen(".txt", "w", stdout);
#endif
	solve();
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章