POJ 1236:Network of Schools

题目链接:http://poj.org/problem?id=1236

题目翻译:

给出N个学校(编号1~N),然后下面N行,第i行后面有若干数,代表第i个学校可以传送新软件

到列表所给出的学校,列表信息以0代表结束。题目中说了这是一个有向图,即u可以给v发送

新软件,而v确不一定能给u发送新软件。问两个问题:subtaskA 和 subtaskB

subtask A:问至少给几个学校发送新软件,就可以使所有学校都可以获得新软件

subtask B:问至少添加多少条关系,可以达到给任意一个学校发送新软件,其他学校都能收到新软件。


解题思路:

1.对于subtask A,我们可以先通过对图进行缩点,对强连通分量进行编号,这样就生成了一个有向无

环图。很容易想到那些学校是必须给他拷贝新软件的,即新的图中那些入度为0的点,因为入度为0的

顶点,他们获取新软件不能由其他学校送达,则可以先给这些点发送新软件,然后由他们传送给其他

学校,即subtask A的答案就是有向无环图中入度为0的顶点个数。


2.对于subtask B,就是给有向图加边,至少加多少条边,可以使图中任意两点之间都有路径。我们知道

对于一个强连通图,任意一个顶点的入度和出度不可能是0,因此我们统计有向无环图的入度为0的顶点

数,出度为0的顶点数。假设为m,n,则答案就是max(m,n),因为最好的方案就是从出度为0的点引边到入

度为0的点,就解决了这个问题,但是由于m,n是可能不等的,最终肯定会余下只有出度为0的点,或入度

为0的点,我们还要解决这些顶点出、入度为0的情况,必然会从他们出发引abs(m-n)条边到别处。

特殊情况:当图本身强连通的时候,不需要加边。


我觉得直接判断原图的出度入度也是可以的吧,但是还是判断图是否是强连通图。


C/C++代码:

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

using namespace std;

int N;                  ///学校的数量
const int maxm = 20000;
int head[maxm];         ///头节点
int Stack[maxm];        ///栈
int inStack[maxm];      ///标记顶点是否在栈中。
int dfn[maxm];          ///时间戳
int low[maxm];          ///作用类似并查集
int belong[maxm];       ///对于每个顶点我们给其编号
int indegree[maxm];     ///统计有向无环图顶点的入度。
int outdegree[maxm];    ///统计有向无环图的顶点的出度。
int edgeNumber,time,top,cnt;
///edgeNumber构成图的边数,time时间标记,top栈顶指针,cnt强连通分量的个数
struct Edge
{
    int u;
    int v;
    int nex;
}edge[maxm];
///加边函数
void addEdge(int u,int v)
{
    ///头插法建图
    edge[edgeNumber].u = u;
    edge[edgeNumber].v = v;
    edge[edgeNumber].nex = head[u];
    head[u] = edgeNumber++;
}
///找强连通分量缩点
void tarjan(int u)
{
    dfn[u] = low[u] = ++time;
    Stack[++top] = u;   ///顶点u入栈
    inStack[u] = 1;     ///标记顶点已经在栈中
    for(int i = head[u]; i != -1; i = edge[i].nex)
    {
        int v = edge[i].v;
        if(!dfn[v])     ///顶点v还没有被访问过
        {
            tarjan(v);
            low[u] = min(low[u],low[v]);
        }
        else if(inStack[v])  ///如果已经访问过在栈中
        {
            low[u] = min(low[u],dfn[v]);
        }
    }
    if(dfn[u] == low[u])
    {
        cnt++;      ///cnt当前强连通分量的编号
        int temp;
        do
        {
            temp = Stack[top--];
            inStack[temp] = 0;
            belong[temp] = cnt;
        }while(temp != u);
    }
}
void solve()
{
    top = time = cnt = 0;
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(inStack,0,sizeof(inStack));
    for(int i = 1; i <= N; i++)
    {
        if(!dfn[i])
        {
            tarjan(i);
        }
    }
    memset(indegree,0,sizeof(indegree));
    memset(outdegree,0,sizeof(outdegree));
    for(int i = 0; i < edgeNumber; i++)
    {
        int u = edge[i].u;
        int v = edge[i].v;
        u = belong[u];
        v = belong[v];
        if(u != v)
        {
            indegree[v]++;
            outdegree[u]++;
        }
    }
    if(cnt == 1)   ///是强连通图
    {
        printf("1\n");
        printf("0\n");
    }
    else
    {
        int zeroIndegreeNode = 0;  ///入度为0的顶点个数
        int zeroOutdegreeNode = 0; ///出度为0的顶点个数
        for(int i = 1; i <= cnt; i++)
        {
            if(indegree[i]==0)
                zeroIndegreeNode++;
            if(outdegree[i]==0)
                zeroOutdegreeNode++;
        }
        printf("%d\n",zeroIndegreeNode);
        printf("%d\n",max(zeroIndegreeNode,zeroOutdegreeNode));
    }
}
int main()
{
    while(~scanf("%d",&N))
    {
        memset(head,-1,sizeof(head));
        edgeNumber = 0;
        for(int i = 1; i <= N; i++)
        {
            int v;
            bool flag = true;
            while(flag)
            {
                scanf("%d",&v);
                if(v == 0)
                    break;
                addEdge(i,v);
            }
        }
        solve();
    }
    return 0;
}


java代码:

package Unit4;

import java.util.Arrays;
import java.io.*;
import java.util.*;

/*边类型*/
class Edge{
	int u;   ///记录u->v的一条边
	int v;
	int nex;
	///构造函数,对边进行初始化
	Edge(int u,int v,int nex){
		this.u = u;
		this.v = v;
		this.nex = nex;
	}
}
/*图类,存放有向图*/
class Graph{
	int edgeNumber;    //图的边数
	final int MAXM = 20000;
	Edge[] edge = new Edge[MAXM];  ///图的边
	int[] head = new int[MAXM];    ///链表头节点
	/*构造函数完成初始化工作*/
	Graph() {
		Arrays.fill(head, -1);
		edgeNumber = 0;
	}
	//加边函数,作用是给图加边
	void addEdge(int u,int v) {
		edge[edgeNumber] = new Edge(u,v,head[u]);
		head[u] = edgeNumber++;
	}
	///获取头节点
	int getHead(int u) {
		return head[u];
	}
	///获取下一条边的位置
	int getNextEdge(int index) {
		return edge[index].nex;
	}
	///获取边的终点
	int getNextNode(int index){
		return edge[index].v;
	}
	///获取边的起点
	int getNowNode(int index){
		return edge[index].u;
	}
}
/*自写栈*/
class Stack {
	int[] st = new int[20000];
	boolean[] vis = new boolean[20000];  //用来标记顶点是否在栈中
	int top;                             //栈顶指针
	///对栈进行初始化
	Stack() {
		top = 0;
		Arrays.fill(vis, false);
	}
	///元素入栈,并标记改节点在栈中
	void pushElement(int e) {
		st[++top] = e;
		vis[e] = true;
	}
	///获取栈顶元素
	int getTop() {
		return st[top];
	}
	///元素出栈,出栈并取消标记
	void popElement() {
		vis[st[top]] = false;
		top--;
	}
	///判断某个节点是否在栈中
	boolean inStack(int e)
	{
		return vis[e];
	}
}
/*tarjan算法*/
class Tarjan {
	final int maxm = 20000;
	Graph G;  ///图
	Stack s;  ///栈
	int[] dfn = new int[maxm];  ///时间标记
	int[] low = new int[maxm];  ///类似并查集
	int[] belong = new int[maxm]; 
	int time;                   ///时间戳
	int cnt;                    ///图中强连通分量个数
	///构造函数进行初始化
	Tarjan(Graph g) {
		G = new Graph();
		G = g;
		s = new Stack();
		Arrays.fill(dfn,0);
		Arrays.fill(low, 0);
		time = 0;
		cnt = 0;
	}
	///深搜找强连通分量
	void dfstarjan(int u) {
		dfn[u] = low[u] = ++time;
		s.pushElement(u);   ///进栈并标记
		for(int i = G.getHead(u); i != -1; i = G.getNextEdge(i)) {
			int v = G.getNextNode(i);
			if(dfn[v]==0) {
				dfstarjan(v);
				low[u] = Math.min(low[u],low[v]);
			}
			else if(s.inStack(v)) {
				low[u] = Math.min(low[u], dfn[v]);
			}
		}
		if(dfn[u] == low[u]) {
			cnt++;           ///cnt为连通分量个数
			int temp;
			do {
				temp = s.getTop();
				s.popElement();
				belong[temp] = cnt;
			}while(temp != u);
		}
	}
	void solve(int N) {
		for(int i = 1; i <= N; i++) {
			if(dfn[i]==0)
				dfstarjan(i);
		}
	}
	///获取节点所属的强连通分量编号
	int getBelong(int v) {
		return belong[v];
	}
}
///求解类
class Solve {
	int N;
	Graph g;
	int[] indegree = new int[20000];  ///统计节点入度
	int[] outdegree = new int[20000]; ///统计节点出度
	static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	static StreamTokenizer input = new StreamTokenizer(br);
	static PrintWriter output = new PrintWriter(new OutputStreamWriter(System.out));
	void calculate() throws IOException{
		int u,v;
		while(input.nextToken() != input.TT_EOF){
			N = (int) input.nval;
			g = new Graph();
			for(int i = 1; i <= N; i++) 
			{
				while(true) {
					v = nextInt();
					if(v == 0)
						break;
					else 
						g.addEdge(i, v);
				}
			}
		}
		Tarjan t = new Tarjan(g);
		t.solve(N);
		Arrays.fill(indegree,0);
		Arrays.fill(outdegree, 0);
		for(int i = 0; i < g.edgeNumber; i++) {
			v = g.getNextNode(i);
			u = g.getNowNode(i);
			u = t.getBelong(u);
			v = t.getBelong(v);
			if(u != v) {
				outdegree[u]++;
				indegree[v]++;
			}
		}
		int zeroIndegreeNode = 0;
		int zeroOutdegreeNode = 0;
		for(int i = 1; i <= t.cnt; i++) {
			if(indegree[i]==0)
				zeroIndegreeNode++;
			if(outdegree[i]==0)
				zeroOutdegreeNode++;
		}
		if(t.cnt == 1){
			output.println("1");
			output.println("0");
		}
		else {
			output.println(zeroIndegreeNode);
			output.println(Math.max(zeroIndegreeNode, zeroOutdegreeNode));
		}
		output.flush();
		output.close();
	}
	static String next() throws IOException {
		input.nextToken();
		return input.sval;
	}
	static char nextChar() throws IOException {
		input.nextToken();
		return input.sval.charAt(0);
	}
	static long nextLong() throws IOException {
		input.nextToken();
		return (long) input.nval;
	}
	static int nextInt() throws IOException {
		input.nextToken();
		return (int) input.nval;
	}
}
public class Main {
	public static void main(String args[]) throws IOException{
		Solve so = new Solve();
		so.calculate();
	}
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值