zoukankan      html  css  js  c++  java
  • POJ1236:Network of Schools (思维+Tarjan缩点)

    Network of Schools

    Time Limit: 1000MS   Memory Limit: 10000K
    Total Submissions: 24880   Accepted: 9900

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

    Description:

    A number of schools are connected to a computer network. Agreements have been developed among those schools: each school maintains a list of schools to which it distributes software (the “receiving schools”). Note that if B is in the distribution list of school A, then A does not necessarily appear in the list of school B 
    You are to write a program that computes the minimal number of schools that must receive a copy of the new software in order for the software to reach all schools in the network according to the agreement (Subtask A). As a further task, we want to ensure that by sending the copy of new software to an arbitrary school, this software will reach all schools in the network. To achieve this goal we may have to extend the lists of receivers by new members. Compute the minimal number of extensions that have to be made so that whatever school we send the new software to, it will reach all other schools (Subtask B). One extension means introducing one new member into the list of receivers of one school. 

    Input:

    The first line contains an integer N: the number of schools in the network (2 <= N <= 100). The schools are identified by the first N positive integers. Each of the next N lines describes a list of receivers. The line i+1 contains the identifiers of the receivers of school i. Each list ends with a 0. An empty list contains a 0 alone in the line.

    Output:

    Your program should write two lines to the standard output. The first line should contain one positive integer: the solution of subtask A. The second line should contain the solution of subtask B.

    Sample Input:

    5
    2 4 3 0
    4 5 0
    0
    0
    1 0

    Sample Output:

    1
    2

    题意:

    给出一个有向图,然后要你输出两个任务的答案:

    1.至少需要从多少个点出发,能够到达所有的点;2.最少需要连多少条边,能够使得从任意点出发都能够到达其它所有点。

    题解:

    这个题我一开始想的就是直接暴力,但很明显第二个问题行不通,所以就要考虑一些性质,或者用一些数学思想。

    第一个问题还是比较好想,入度为0的点的个数即位答案,如果不存在入度为0的点,答案就是1。简略证明如下(题目保证图是连通的):

    假设入度为0的点为n,那么至少需要n个点才能遍及所有点,然后对于其余入度非0的点来说,必然是由其他点到达的,如果这个点不在环上,那么就必定是从一个入度为0的点来的;如果这个点在环上,这个环中的所有点也会由其余入度为0的点到达;假设这是个单独的环,那么答案为1。

    第二个问题要求所有点都互相可以到达。那么我们可以知道的是,图中必然不会存在入度为0以及出度为0的点,假设这两者的个数分别为n,m。

    那么最优的连边方法就是入度为0的点与出度为0的点匹配,最后剩下的乱连就行了,所以最后答案就是max(n,m)。证明的话yy一下吧。

    因为我们刚才是基于有向无环图来思考的,环的存在应该把它当作一个点,所以考虑Tarjan缩波点就行了。

    代码如下:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <iostream>
    #include <queue>
    #include <stack>
    using namespace std;
    typedef long long ll;
    const int N = 105;
    int n,tot;
    int head[N],in[N],out[N],low[N],dfn[N],vis[N],scc[N];
    struct Edge{
        int u,v,next;
    }e[N*N<<1],edge[N*N<<1];
    void adde(int u,int v){
        e[tot].u=u;e[tot].v=v;e[tot].next=head[u];head[u]=tot++;
    }
    stack <int> s;
    int T,num;
    void Tarjan(int u){
        dfn[u]=low[u]=++T;vis[u]=1;
        s.push(u);
        for(int i=head[u];i!=-1;i=e[i].next){
            int v=e[i].v;
            if(!vis[v]){
                Tarjan(v);
                low[u]=min(low[u],low[v]);
            }else if(!scc[v]){
                low[u]=min(low[u],dfn[v]);
            }
        }
        if(low[u]==dfn[u]){
            num++;int now;
            do{
                now = s.top();s.pop();
                scc[now]=num;
            }while(!s.empty() && now!=u);
        }
    }
    int main(){
        scanf("%d",&n);
        int m=0;
        memset(head,-1,sizeof(head));
        for(int i=1;i<=n;i++){
            int v;
            while(scanf("%d",&v)!=EOF){
                if(v==0) break ;
                edge[++m].u=i;edge[m].v=v;
                adde(i,v);
            }
        }
        //cout<<m<<endl;
        for(int i=1;i<=n;i++){
            if(!vis[i]) Tarjan(i);
        }
        for(int i=1;i<=m;i++){
            int u=edge[i].u,v=edge[i].v;
            if(scc[u]!=scc[v]){
                in[scc[v]]++;out[scc[u]]++;
            }
        }
        int cnt1=0,cnt2=0;
        for(int i=1;i<=num;i++){
            if(!in[i]) cnt1++;
            if(!out[i]) cnt2++;
        }
        //cout<<num<<endl;
        if(num==1) cout<<1<<endl<<0;
        else cout<<cnt1<<endl<<max(cnt2,cnt1);
        return 0;
    }
  • 相关阅读:
    如何在仪表板中播放视频
    根据条件显示表格背景色
    如何个性化设置柱形图(条形图)柱子粗细、圆角和颜色?
    如何设置报表参数的日期区间?
    妙用这3个函数,实现各种你意想不到的条件格式化
    利用组合图对数据按百分比区间进行对比展示的实例
    报表传参跳转到仪表板的实现过程
    Wyn仪表板中的图表,如何针对每个字段设置自定义颜色
    Wyn仪表板中设置完整数据格式的方法
    Wyn门户及仪表板颜色设置方式汇总
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/10393053.html
Copyright © 2011-2022 走看看