zoukankan      html  css  js  c++  java
  • hdu 3639 HawkandChicken (强连通 + 反图 + 缩点) && hdu1827 Summer Holiday && hdu 1269 迷宫城堡 && hdu3072 Intelligence System

    hdu1827 
    #include<iostream>
    #include<algorithm>
    #include<stack>
    #include<vector>
    #define MAXN 1010
    using namespace std;
    vector<int> g[MAXN],g2[MAXN];
    stack<int> st;
    int n,w[MAXN],dfn[MAXN],low[MAXN],f[MAXN],index;
    int in[MAXN],num,sccw[MAXN];
    bool vis[MAXN],instack[MAXN];
    void tarjan(int u)//求强连通分支
    {
    int v;
    dfn[u] = low[u] = index++;
    st.push(u);
    instack[u] = true;
    vis[u] = true;
    for(int i=0; i<g[u].size(); i++)
    {
    v = g[u][i];
    if(!vis[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])
    {
    sccw[num]=INT_MAX;
    do
    {
    v = st.top();
    instack[v] = false;
    st.pop();
    f[v]=num;//记录每一个点所在的强连通分支
    sccw[num]=min(w[v],sccw[num]);//选择花费最少的人最为该连通分支的代表
    }
    while(v != u);
    num++;
    }
    }
    int main()
    {
    int m,a,b;
    while(scanf("%d %d",&n,&m)==2)
    {
    for(int i=0;i<=n;i++)
    {
    g[i].clear();
    g2[i].clear();
    }
    for(int i=1;i<=n;i++)
    scanf("%d",&w[i]);
    while(m--)
    {
    scanf("%d %d",&a,&b);
    g[a].push_back(b);
    }
    memset(vis,false,sizeof(vis));
    memset(instack,false,sizeof(instack));
    num=0;
    for(int i=1;i<=n;i++)
    {
    if(!vis[i])
    tarjan(i);
    }
    memset(in,0,sizeof(in));
    for(int i=1;i<=n;i++)
    for(int j=0;j<g[i].size();j++)
    {
    if(f[i]!=f[g[i][j]])
    {
    g2[f[i]].push_back(f[g[i][j]]);
    in[f[g[i][j]]]++;
    }
    }
    int ans=0,sum=0;
    for(int i=0;i<num;i++)
    if(in[i]==0)
    {
    ans++;
    sum+=sccw[i];
    }
    printf("%d %d\n",ans,sum);
    }
    return 0;
    }


    hdu3639

    题意:就是有一个有向图,他们是有传递关系的,问对于图中哪些点是其他最多点能到达他的。

    分析:有俩个关键点,第一:就是求一个强连通分支,关于求强连通分支的tarjan 算法,这个博客解释的很详细http://www.cnblogs.com/liyongmou/archive/2010/08/14/1799436.html

    因为一个强连通分支里面,任意俩点都是可达的,所有,同一个连通分支里面的点,可到达该点的点数是一样的,这样就可以将一个连通分支缩成一个点

    第二:将缩点后的图反向,这样,到达一个点的点数最多的肯定是在入度为0 的强连通分支

    hdu3639 
    #include<iostream>
    #include<algorithm>
    #include<stack>
    #include<vector>
    #define MAXN 5010
    using namespace std;
    vector<int> g[MAXN],g2[MAXN];//g保存原图,g2保存缩点反向后的图
    stack<int> st;
    int n,index,num,dfn[MAXN],low[MAXN],f[MAXN];
    bool vis[MAXN],instack[MAXN];
    int ans[MAXN],in[MAXN],scc[MAXN],sum;
    void tarjan(int u)//求强连通分支
    {
    int v;
    dfn[u] = low[u] = index++;
    st.push(u);
    instack[u] = true;
    vis[u] = true;
    for(int i=0; i<g[u].size(); i++)
    {
    v = g[u][i];
    if(!vis[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])
    {
    do
    {
    v = st.top();
    instack[v] = false;
    st.pop();
    f[v]=num;//记录每一个点所在的强连通分支
    scc[num]++;//保存该强连通分支的点数
    }
    while(v != u);
    num++;
    }
    }
    void dfs(int u)//深搜求出可到达该连通分支的点数
    {
    vis[u]=true;
    sum+=scc[u];
    for(int i=0;i<g2[u].size();i++)
    if(!vis[g2[u][i]])
    dfs(g2[u][i]);
    }
    int main()
    {
    int T,m,a,b,cas=0;
    scanf("%d",&T);
    while(T--)
    {
    scanf("%d %d",&n,&m);
    for(int i=0;i<=n;i++)//注意要将向量清空
    {
    g[i].clear();
    g2[i].clear();
    }
    while(m--)
    {
    scanf("%d %d",&a,&b);
    g[a].push_back(b);
    }
    index=num=0;
    memset(vis,false,sizeof(vis));
    memset(instack,false,sizeof(instack));
    memset(scc,0,sizeof(scc));
    for(int i=0;i<n;i++)
    {
    if(!vis[i])
    tarjan(i);
    }
    memset(in,0,sizeof(in));
    for(int i=0;i<n;i++)//重新构图
    for(int j=0;j<g[i].size();j++)
    {
    if(f[i]!=f[g[i][j]])
    {
    g2[f[g[i][j]]].push_back(f[i]);
    in[f[i]]++;
    }
    }
    int maxans=-1;
    memset(ans,-1,sizeof(ans));
    printf("Case %d: ",++cas);
    for(int i=0;i<num;i++)
    {
    if(in[i]==0)
    {
    sum=0;
    memset(vis,false,sizeof(vis));
    dfs(i);
    ans[i]=sum;
    if(sum>maxans)
    maxans=sum;
    }
    }
    printf("%d\n",maxans-1);
    int flag=0;
    for (int i = 0; i < n; i++)
    if ( ans[f[i]] == maxans)
    {
    if(!flag){
    printf("%d", i);
    flag=1;
    }
    else printf(" %d",i);
    }
    printf("\n");
    }
    return 0;
    }

     hdu1827

    中文题,题意就不说了

    分析:要求出最小的联系人数,首先,我们知道,在一个强连通图里面,任意俩个人都是可达的,所以,对于一个强连通分支,可以选择一个花费最少的人作为代表,缩成一个点。

    求出所有强连通分量之后,所有入度为0 的强连通分支都是必须联系的。所以,最小花费和最少人数都满足了

     hdu1269

    很直接了,判断强连通分量是否等于1

    View Code
    #include<iostream>
    #include<algorithm>
    #include<stack>
    #include<vector>
    #define MAXN 10010
    using namespace std;
    vector<int> g[MAXN];
    stack<int> st;
    int n,dfn[MAXN],low[MAXN],index;
    int num;
    bool vis[MAXN],instack[MAXN];
    void tarjan(int u)//求强连通分支
    {
    int v;
    dfn[u] = low[u] = index++;
    st.push(u);
    instack[u] = true;
    vis[u] = true;
    for(int i=0; i<g[u].size(); i++)
    {
    v = g[u][i];
    if(!vis[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])
    {
    do
    {
    v = st.top();
    instack[v] = false;
    st.pop();
    }
    while(v != u);
    num++;
    }
    if(num>=2)
    return;
    }
    int main()
    {
    int m,a,b;
    while(scanf("%d %d",&n,&m)==2 && (n||m))
    {
    for(int i=0;i<=n;i++)
    g[i].clear();
    while(m--)
    {
    scanf("%d %d",&a,&b);
    g[a].push_back(b);
    }
    memset(vis,false,sizeof(vis));
    memset(instack,false,sizeof(instack));
    num=0;
    for(int i=1;i<=n;i++)
    {
    if(!vis[i])
    tarjan(i);
    if(num>=2)
    break;
    }
    if(num==1)
    puts("Yes");
    else puts("No");
    }
    return 0;
    }


     hdu3072 Intelligence System

    题意:强连通缩点后,内部的值无效,求缩点后的有向无环图的,到达所有点的最短距离和

    做法:强连通模板(缩点),寻找 每个点的 从其他点到该点的最小权值的边

    View Code
    #include<iostream>
    #include<stack>
    #include<algorithm>
    #include<vector>
    #define MAXN 50010
    using namespace std;
    stack<int> st;
    int n,num,dfn[MAXN],low[MAXN],f[MAXN],index;
    bool vis[MAXN],instack[MAXN];
    int sum,ans[MAXN];
    struct edge1
    {
    int v,w;
    edge1(int a=0,int c=0):v(a),w(c){}
    };
    vector<edge1> g[MAXN];
    void tarjan(int u)
    {
    vis[u]=true;
    st.push(u);
    instack[u]=true;
    dfn[u]=low[u]=index++;
    int v;
    for(int i=0;i<g[u].size();i++)
    {
    v=g[u][i].v;
    if(!vis[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])
    {
    do
    {
    v=st.top();
    f[v]=num;
    st.pop();
    instack[v]=false;
    }while(v!=u);
    num++;
    }
    }
    int main()
    {
    int m,a,b,c;
    while(scanf("%d %d",&n,&m)==2)
    {
    for(int i=0;i<n;i++)
    g[i].clear();
    while(m--)
    {
    scanf("%d %d %d",&a,&b,&c);
    g[a].push_back(edge1(b,c));
    }
    memset(vis,false,sizeof(vis));
    memset(instack,false,sizeof(instack));
    num=index=0;
    for(int i=0;i<n;i++)
    {
    if(!vis[i])
    tarjan(i);
    }
    for(int i=0;i<num;i++)
    ans[i]=INT_MAX;
    for(int i=0;i<n;i++)
    {
    for(int j=0;j<g[i].size();j++)
    {
    int v=g[i][j].v;
    if(f[i]!=f[v])
    {
    ans[f[v]]=min(ans[f[v]],g[i][j].w);
    }
    }
    }
    sum=0;
    for(int i=0;i<num;i++)
    if(ans[i]!=INT_MAX)
    sum+=ans[i];
    printf("%d\n",sum);
    }
    return 0;
    }
  • 相关阅读:
    我的开发环境配置经验
    C# WINFORM 打包数据库
    C#格式化数值结果表(格式化字符串)
    Excel如何固定表头,任意一行
    下载fiddler证书并设置信任
    fiddler展示serverIP方法
    fiddler抓包参数乱码的解决方法
    fiddler模拟发送get/post请求(也可做简单接口测试)
    Jenkins常用插件
    关于gitignore文件的创建与使用
  • 原文地址:https://www.cnblogs.com/nanke/p/2367334.html
Copyright © 2011-2022 走看看