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

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;
}
原文地址:https://www.cnblogs.com/xwww666666/p/11420155.html