• poj 2186 && hdu 3836


    大体思路都是找强连通子图,缩点,只是两个题目的所求不是太一样。

    hdu 3836:

    参考了qianshou的代码,网上的很多看的不是很懂,他写的很清晰,很好看懂

    http://blog.csdn.net/wsniyufang/article/details/6604503

    然后就是还要了解一点Tarjan算法,下面的那个网址讲的很清楚

    http://www.byvoid.com/blog/scc-tarjan/

    剩下的就是要自己写一个getans()的求结果的程序。

    经过缩点后,剩下的就是一个DAG图了。

    至于为什么是取入度和出度为0的点中最大的,我想了很久,还是不是特别清楚。

    我是这么想的,剩下的图如果要全联通,那么每个点必须有入度和出度,如果出度为0 的点多于入度为0的点。那么可以让度为0的点指向

    入度为0的点和其他的点,从而让每个点都有入度和出度。反之亦然。

    感觉是对的,但不知道这样想对不对,如果路过的大牛有好的观点,希望给予指正

    /*

    HDU 3836
    题目大意:给出一个有向图,求最少添加多少条边使得该图变成强连通图。

    首先如果图本身就是强连通那么答案为0。

    否则先缩强连通分量将图变为DAG,然后算出入度为0的点和出度为0的点的个数,取最大值即为答案。

    2011-7-14
    */

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #define mMax 50010
    #define nMax 20010
    using namespace std;
    struct Edge        //
    {
    int v, x;
    }edge[mMax];
    int cnt, top, ans, n, m, e;
    int belong[nMax], dfn[nMax], low[nMax], Stack[nMax], head[nMax], out[nMax], in[nMax];
    bool instack[nMax];

    void BuildGraph(int from, int to)    //建图
    {
    edge[e].v=to;
    edge[e].x=head[from];
    head[from]=e;
    e++;
    }

    void init()    //初始化及输入
    {
    memset(head, -1, sizeof(head));
    memset(belong, 0, sizeof(belong));
    memset(low, 0, sizeof(low));
    memset(dfn, 0, sizeof(dfn));
    memset(out, 0, sizeof(out));
    memset(in, 0, sizeof(in));
    memset(instack, 0, sizeof(instack));
    int a, b; e=0;
    for(int i=0; i<m; i++)
    {
    scanf("%d%d", &a, &b);
    BuildGraph(a ,b);
    }
    }

    int getans()    //输出
    {
    int ct[nMax]; //ct[i]=j:强连通分支i有j个点
    int v;
    memset(ct, 0, sizeof(ct));
    if(cnt==1) return 0;
    //枚举每一个点,求outdegree和ct
    for(int i=1; i<=n; i++)
    {
    ct[belong[i]]++;
    for(int k=head[i]; k!=-1; k=edge[k].x)
    {
    v=edge[k].v;
    if(belong[i]!=belong[v])
    {
    out[belong[i]]++;    //出度
    in[belong[v]]++;     //入度
    }
    }
    }
    int num_in=0, num_out=0;
    for(int i=1; i<=cnt; i++)
    {
    if(in[i]==0)
    num_in++;
    if(out[i]==0)
    num_out++;
    }
    return max(num_out, num_in);//入度和出度中最大的
    }

    void dfs(int i)
    {
    int j;
    dfn[i]=low[i]=ans++;
    instack[i]=1;
    Stack[++top]=i;
    //对每条边枚举
    for(int k=head[i]; k!=-1; k=edge[k].x)
    {
    j=edge[k].v;
    //cout<<k<<endl;system("pause");
    if(!dfn[j])
    {
    dfs(j);
    low[i]=min(low[i], low[j]);
    }
    else if(instack[j] && dfn[j]<low[i])
    low[i]=dfn[j];
    }

    //如果i是强连通分量的根
    if(dfn[i]==low[i])
    {
    cnt++;
    do
    {
    j=Stack[top--];
    instack[j]=0;
    belong[j]=cnt;
    }while(j!=i);
    }
    }

    void Tarjan()    //Tarjan算法
    {
    top=cnt=0;ans=1;
    memset(dfn, 0, sizeof(dfn));
    for(int i=1; i<=n; i++)
    if(!dfn[i])
    dfs(i);
    printf("%d\n", getans());
    }

    int main()
    {
    while(scanf("%d%d", &n,&m)!=EOF)
    {
    init();
    Tarjan();
    }
    }

    ;-------------------------------------------------------------

    POJ 2186

    与上题的大体框架一样,我只改动了getans();

    貌似这题的数据很弱,Discuss里还有一些数据,我也试了下,过了。

    注意题目中有关系A->B,B->C可以得到A->C。

    这题就是要找出度为0的点(将奶牛的关系用有向图表示),因为只能有一个,

    所以当出现两个出度为0的点的时候表示至少有一个(一个集合)与当前的

    奶牛合不来,没有最受欢迎的,无解。

    而且,当只有一个出度为0 的点的时候,表示其他所有的奶牛都有指向此奶牛的路径,

    因此,他是最受欢迎的。

    另外,我走了一点弯路,有点想偷懒,想用instack[]数组标记有出度的点,但后来发

    现一旦出现环路,就会得到一个错误的结论,贡献了两次WA

    /*
    题目大意:n头奶牛,给出若干个欢迎关系a b,表示a欢迎b,
    欢迎关系是单向的,但是是可以传递的。另外每个奶牛都是欢
    迎他自己的。求出被所有的奶牛欢迎的奶牛的数目。
    2011-7-14
    */
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #define mMax 50010
    #define nMax 20010
    using namespace std;
    struct Edge
    {
    int v, x;
    }edge[mMax];
    int cnt, top, ans, n, m, e;
    int belong[nMax], dfn[nMax], low[nMax], Stack[nMax], head[nMax], out[nMax], in[nMax];
    bool instack[nMax];

    void BuildGraph(int from, int to)
    {
    edge[e].v=to;
    edge[e].x=head[from];
    head[from]=e;
    e++;
    }

    void init()
    {
    memset(head, -1, sizeof(head));
    memset(belong, 0, sizeof(belong));
    memset(low, 0, sizeof(low));
    memset(dfn, 0, sizeof(dfn));
    memset(out, 0, sizeof(out));
    memset(in, 0, sizeof(in));
    memset(instack, 0, sizeof(instack));
    int a, b; e=0;
    for(int i=0; i<m; i++)
    {
    scanf("%d%d", &a, &b);
    BuildGraph(a ,b);
    }
    }

    int getans()
    {
    memset(instack, 0, sizeof(instack));
    int v;
    if(cnt==1) return n;
    for(int i=1; i<=n; i++)
    {
    for(int k=head[i]; k!=-1; k=edge[k].x)
    {
    v=edge[k].v;
    if(belong[i]!=belong[v])
    {
    out[belong[i]]++;
    }
    }
    }
    int sum=0, num=0, flag=0;
    for(int i=1; i<=cnt; i++)
    {
    if(out[i]==0)
    {
    flag=i;
    num++;
    }
    }
    if(num>1) return 0;
    for(int i=1; i<=n; i++)
    {
    if(belong[i]==flag) sum++;
    }
    return sum;
    }

    void dfs(int i)
    {
    int j;
    dfn[i]=low[i]=ans++;
    instack[i]=1;
    Stack[++top]=i;
    //对每条边枚举
    for(int k=head[i]; k!=-1; k=edge[k].x)
    {
    j=edge[k].v;
    //cout<<k<<endl;system("pause");
    if(!dfn[j])
    {
    dfs(j);
    low[i]=min(low[i], low[j]);
    }
    else if(instack[j] && dfn[j]<low[i])
    low[i]=dfn[j];
    }

    //如果i是强连通分量的根
    if(dfn[i]==low[i])
    {
    cnt++;
    do
    {
    j=Stack[top--];
    instack[j]=0;
    belong[j]=cnt;
    }while(j!=i);
    }
    }

    void Tarjan()
    {
    top=cnt=0;ans=1;
    memset(dfn, 0, sizeof(dfn));
    for(int i=1; i<=n; i++)
    if(!dfn[i])
    dfs(i);
    printf("%d\n", getans());
    }

    int main()
    {
    while(scanf("%d%d", &n,&m)!=EOF)
    {
    init();
    Tarjan();
    }
    return 0;
    }
    作者:FreeAquar
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    MVC过滤器
    MVC自带的校验
    FPGA简单概述
    CAN总线扩展数据帧介绍
    简述人工智能发展的先决条件
    CAN总线标准帧
    CAN总线应用
    CAN总线优点
    CAN总线概述
    高速PCB设计注意事项
  • 原文地址:https://www.cnblogs.com/FreeAquar/p/2106403.html
Copyright © 2020-2023  润新知