POJ

题意:给出一个有向图
输出1:至少要向多少台电脑放文件能使所有电脑都能得到文件
输出2:至少加多少条边,可以使得在任意地方放文件,文件都能到达任意一台电脑
思路:从题意上来看就是 寻找有向图中的强连通分量(极大强连通子图)
输出2即是:在DAG上要加几条边,才能使得DAG变成强连通图
所以使用tarjan算法,我们就把强连通分量缩成一个点,然后再根据这些点的出入度来增加边
假设n个入度为0的点,m个出度为0的点
(1)若m<=n 加了n边后 连接入度和出度为0的点
(2)若m >n,则还有m-n个入度为0的点没有解决,所以还要加上m-n边
即加边为max(m,n); 如果只有一个强连通分量时就不需要加边了

完整代码:(详细注解)

#include <cstdio>
#include <stack>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1e3;
const int maxm = 1e5;
struct Edge{
    int v,next;
    int u;
}edge[maxm];
int head[maxn];
int top,num;
int dfn[maxn],low[maxn],belong[maxn];//belong相当于染色,把同一个连通图标记为一种颜色
int in[maxn],out[maxn];//出入度
int instack[maxn];//标记是否在栈中
int index;
stack<int>Stack;
void tarjan(int x){
    //(1)结点u初值
    low[x] = dfn[x] = ++index;
    instack[x] = 1;
    //(2)压入栈中
    Stack.push(x);
    //(3)枚举每一条边
    for(int i = head[x]; ~i;i= edge[i].next){
     //(4)入过结点u为强连通分量的根节点时
        int v = edge[i].v;
        //(5)没有被访问时候递归搜索,搜到底了之后就并到父节点
        if(!dfn[v]){
            tarjan(v);
            low[x] = min(low[x],low[v]);
        }
        //(5*)如果结点已在栈中就并其时间戳:因为在栈中意味着先就被访问过,也就是该结点的祖先结点
        else if(instack[v]){
         low[x]
= min(low[x],low[v]); } } //(6)找到根部分 if(low[x] == dfn[x]){ int t; ++num;//强连通分量个数 do{ t = Stack.top(); belong[t] = num; //出栈 instack[t] = 0; Stack.pop(); }while(t != x);//直到退出结点位置到其本身 } } void add(int u,int v){ edge[top].v = v; edge[top].next = head[u]; head[u] = top++; } void init(){ memset(head,-1,sizeof(head)); memset(instack,0,sizeof(instack)); memset(in,0,sizeof(in)); memset(out,0,sizeof(out)); // memset(dfn,0,sizeof(dfn)); // memset(low,0,sizeof(low)); // memset(belong,0,sizeof(belong)); } int main(){ init(); int n,x; cin>>n; for(int i = 1;i<=n;i++){ while(cin>>x&&x) add(i,x); } for(int i = 1;i<=n;i++){ //没有被访问就进行tarjan if(!dfn[i]) tarjan(i); } if(num == 1) { cout<<1<<endl<<0<<endl; return 0; } for(int i = 1;i<=n ; i++){ for(int j = head[i]; ~j ; j = edge[j].next){ int v = edge[j].v; //如果不在一个分量中(即缩点之后的结点关系) if(belong[i] != belong[v]){ out[belong[i]]++; in[belong[v]]++; } } } int sum1,sum2; sum1 = sum2 = 0; for(int i = 1; i <= num;i++){ if(!out[i]) sum1++; if(!in[i]) sum2++; } cout<<sum2<<endl<<max(sum1,sum2)<<endl; return 0; }
原文地址:https://www.cnblogs.com/Tianwell/p/11313731.html