• 【图论】【刷题】【蓝绿题】【强连通】【拓扑】


    1>神经网络

    绿题

    为什么用拓扑?

    1)必备条件:DAG
    2)本题的公式上,显示需要计算出能连接到本点的,全部的边值*点值
    故而当我开始推这个点时,他的所有先驱点的状态全部都要求完
    这只能是topo_sort

    #include<cstdio>
    #include<cstdlib>
    #include<queue>
    #include<vector>
    using namespace std;
    int n,m;
    const int N=103;
    int c[N],in[N],sz[N];
    struct node
    {
        int v,w;
        node(int vv,int ww)
        { v=vv,w=ww; }
        node(){}
    };
    vector <node > g[N];
    queue <int > q;
    
    void topo_sort()
    {
        while(!q.empty())
        {
            int t=q.front();q.pop();
            sz[t]=g[t].size() ;
            
            for(int i=0;i<sz[t];i++)
            {
                node v=g[t][i];
                if(c[t]>0) c[v.v ]+=c[t]*v.w ;
                if(--in[v.v ]==0) q.push(v.v );
            }
        }
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        int x;
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&c[i],&x);
            if(c[i]>0) q.push(i);
            else c[i]-=x;
        }
        int u,v,w;
        while(m--)
        {
            scanf("%d%d%d",&u,&v,&w);
            g[u].push_back(node(v,w)); 
            in[v]++;
        }
        
        topo_sort();
        bool fail=true;
        for(int i=1;i<=n;i++)
            if(!sz[i] && c[i]>0)
            {
                printf("%d %d
    ",i,c[i]);
                fail=false;
            }
        if(fail) printf("NULL
    ");
        return 0;
    } 

    2>校园网络

    >难点:
    DAG中,如何加入最少的边,使图中所有边都在一个强连通分量中
    >试着从in,out的方面分析问题,
    加边:入度+1,出度+1
    >推测 :
    缩点 后的任意有向图
    若要向其中添加一些边,使得仅存一个强连通分量,
    添加边的数目min= max(入度为0的点 , 出度为0的点)
    >证明:(luogu)

    一个任意DAG,必定是由多个链形成的,必然有起点u和终点v

    我们先建立u->v的边,嗯链少了一条,变成了点,

    入度为0的点-1,出度为0的点-1

    重复以上步骤,

    直到最后所有的入度为0的点,出度为0的点,都消失了,

    再看看图,就没有链了,都是环,或者说最后只剩下一个强连通分量

    #include<cstdio>
    #include<cstdlib>
    #include<vector>
    #include<stack>
    #include<algorithm>
    using namespace std;
    
    int n,sum;
    const int N=10003;
    vector <int > g[N];
    int fa[N],sz[N],dfn[N],low[N],tt;
    stack <int> s;
    bool f[N];
    void tarjan (int x)
    {
        dfn[x]=low[x]=++tt;
        f[x]=true,s.push(x);
        
        sz[x]=g[x].size() ;
        for(int i=0;i<sz[x];i++)
        {
            int v=g[x][i];
            if(!dfn[v])
            {
                tarjan(v);
                low[x]=min(low[x],low[v]);
            }
            else if(f[v])
                low[x]=min(low[x],low[v]);
        }
        
        if(dfn[x]==low[x])
        {
            ++sum;
            while(s.top()!=x)
                f[s.top()]=false,fa[s.top()]=sum,s.pop();
            
            f[x]=false,fa[x]=sum,s.pop() ;
        }
    }
    
    int in[N],out[N];//存sum节点的入度 
    int main()
    {
        scanf("%d",&n);
        int x;
        for(int i=1;i<=n;i++)
            while(~scanf("%d",&x) && x)
                g[i].push_back(x); 
        
        for(int i=1;i<=n;i++)
            if(!dfn[i]) tarjan(i);
        for(int i=1;i<=n;i++)
        {
            int fu=fa[i],fv;
            for(int j=0;j<sz[i];j++)
            {
                int v=g[i][j];
                fv=fa[v];
                if(fu!=fv) in[fv]++,out[fu]++;
            }
        }
        
        int ans1=0,ans2=0;
        for(int i=1;i<=sum;i++)
        {
            if(!in[i]) ans1++;
            if(!out[i]) ans2++; 
        }
        printf("%d
    %d",ans1,max(ans1,ans2));
        
        return 0;
    }

    3>旅行计划

    这道题的题面很有意思啊
    所有城市之所以能走,有两个条件
    1)u在v的西面
    2)u和v之间有一条边
    所以其实这个拓扑序是真的很明显了,
    所求出的拓扑序,其实是东到西的一条街啊

    很巧的有以下性质:
    1)无环,不强连通
    2)无后效性,我到u城市走的是最远的路,再到v城市,就不会再遇到前面的城市

    多好的topo_sort啊

     好水啊

    #include<cstdio>
    #include<cstdlib>
    #include<queue>
    #include<vector>
    using namespace std;
    int n,m;
    const int N=100003;
    vector <int> g[N];
    
    int in[N],ans[N];
    queue <int> q ;
    void topo_sort()
    {
        for(int i=1;i<=n;i++)
            if(!in[i]) q.push(i); 
        
        while(!q.empty() )
        {
            int u=q.front() ;q.pop() ;
            
            int sz=g[u].size() ;
            for(int i=0;i<sz;i++)
            {
                int v=g[u][i];
                ans[v]=ans[u]+1;//是总起点开始的,所以这个有点像bfs,步数再增加或者不变
                if(--in[v]==0) q.push(v); 
            }        
        }
        
        for(int i=1;i<=n;i++)
            printf("%d
    ",ans[i]+1); 
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        int u,v;
        while(m--)
        {
            scanf("%d%d",&u,&v);
            g[u].push_back(v); 
            in[v]++;
        }
        
        topo_sort();
        
        return 0;
    }

    4>排序

    给你n个字母,m个大小限制,

    求出正确的排序

    附加要求:

    1)判断矛盾关系,输出矛盾出现的条件序号(就是第一个错误)

    2)判断准确的大小关系,如果没有只能输出另一种结果!

    所以我们要实现

    1)在线判环。 

    最多是一颗生成树啊,25条边,

    破圈法也行,

    不过我用的是状压+floyd,也不知道是哪来的想法,大概是最近搜索剪枝写多了

    using namespace std;
    char s[10];
    
    int n,m,cnt;
    const int N=30;
    bitset <N> bs[N],in[N];
    bool vis[N];
    void floyd(int x) { for(int i=1;i<=n;i++) if(bs[i][x] && (bs[i]|bs[x])!=bs[i]) bs[i]|=bs[x],floyd(i); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%s",s); int u=s[0]-'A'+1,v=s[2]-'A'+1; if(bs[v][u] || u==v) { printf("Inconsistency found after %d relations.",i); return 0; } bs[u][v]=1; floyd(v); in[v][u]=1; if(cnt<n) { if(!vis[u]) vis[u]=true,cnt++; if(!vis[v]) vis[v]=true,cnt++;; } if(cnt==n) topo_sort(i); } printf("Sorted sequence cannot be determined."); return 0; }

    我代码中,开始拓扑排序有两个条件

    1)每个点都被边涉及到,不是指出,就是指入

    用到了一个cnt,一个vis数组

    2)没有回边,floyd在线更新(这里要剪枝,只有这个点变了,才会继续往上走)

    这样就解决了矛盾的问题

    然后讨论另外两种情况

    1)边够了,topo却不能确定准确顺序

    思考这样会出现什么情况:

    就是topo中出现两个深度相同的点,

    就是一对兄弟,都知道是爸妈生的,但是不知道谁先出生,

    这样就出现了两个nx,return 但不要结束程序

    2)出现了准确的排序

    打印,exit(0),完成

    bitset <N> tt;
    int sum,d[N],inn[N];
    void topo_sort(int step)
    {
        tt.reset();
        for(int i=1;i<=n;i++) tt|=bs[i];
        sum=n-tt.count();
        if(sum!=1) return ;
        
        for(int i=1;i<=n;i++) inn[i]=in[i].count();
        
        int nw,nx; 
        for(nw=1;nw<=n;nw++) 
            if(!tt[nw]) break;
        d[sum=1]=nw;
        while(sum<n)
        {
            nx=0;
            for(int i=1;i<=n;i++)
            {
                if(!bs[nw][i]) continue;
                
                if(--inn[i]==0) 
                {            
                    if(!nx) nx=i;
                    else return ;
                } 
            }
            nw=nx;
            d[++sum]=nw;
        }
        
        printf("Sorted sequence determined after %d relations: ",step);
        for(int i=1;i<=n;i++)
            printf("%c",d[i]+'A'-1);
        printf(".");
        exit(0);
    }

    最后再加上当所有边都处理了,还不能有序或矛盾,就输出不确定

    完整版:

    #include<cstdio>
    #include<cstdlib>
    #include<bitset>
    #include<cstring>
    
    using namespace std;
    char s[10];
    
    int n,m,cnt;
    const int N=30;
    bitset <N> bs[N],in[N];
    bool vis[N];
    
    bitset <N> tt;
    int sum,d[N],inn[N];
    void topo_sort(int step)
    {
        tt.reset();
        for(int i=1;i<=n;i++) tt|=bs[i];
        sum=n-tt.count();
        if(sum!=1) return ;
        
        for(int i=1;i<=n;i++) inn[i]=in[i].count();
        
        int nw,nx; 
        for(nw=1;nw<=n;nw++) 
            if(!tt[nw]) break;
        d[sum=1]=nw;
        while(sum<n)
        {
            nx=0;
            for(int i=1;i<=n;i++)
            {
                if(!bs[nw][i]) continue;
                
                if(--inn[i]==0) 
                {            
                    if(!nx) nx=i;
                    else return ;
                } 
            }
            nw=nx;
            d[++sum]=nw;
        }
        
        printf("Sorted sequence determined after %d relations: ",step);
        for(int i=1;i<=n;i++)
            printf("%c",d[i]+'A'-1);
        printf(".");
        exit(0);
    }
    
    void floyd(int x)
    {
        for(int i=1;i<=n;i++)
            if(bs[i][x] && (bs[i]|bs[x])!=bs[i]) 
                bs[i]|=bs[x],floyd(i); 
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%s",s);
            int u=s[0]-'A'+1,v=s[2]-'A'+1;
            
            if(bs[v][u] || u==v)
            {
                printf("Inconsistency found after %d relations.",i);
                return 0;
            }
            bs[u][v]=1;
            floyd(v);
            in[v][u]=1;
            
            if(cnt<n)
            {
                if(!vis[u]) vis[u]=true,cnt++;
                if(!vis[v]) vis[v]=true,cnt++;;
            }
            
            if(cnt==n)
                topo_sort(i);
        }
        
        printf("Sorted sequence cannot be determined.");
        
        return 0;
    }
  • 相关阅读:
    Windows编程--线程的睡眠方式
    Windows编程-- 等待函数
    Windows编程--线程的切换
    Windows编程-- 用户方式中线程的同步关键代码段(临界区)
    Windows编程--挂起和恢复线程的运行
    Windows编程-- 用户方式中线程的同步原子访问:互锁的函数家族
    Windows编程--线程之GetExitCodeThread()
    Windows编程-- 对Critical Section的封装
    Windows编程--线程的身份标识
    如何在oracle中限制返回结果集的大小
  • 原文地址:https://www.cnblogs.com/xwww666666/p/11420155.html
Copyright © 2020-2023  润新知