题目链接: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();
}
}