图论,专为迷途少年而生

版权声明: https://blog.csdn.net/qq_40828060/article/details/82626675

目录


因为它会让你重拾人生方向
(只要别向着图论走去哪都行) __题记     

Topsort

P1038 神经网络

题目很水,就是有点坑
不难发现这是个DAG,且个点的更新有严格的顺序要求(因此不能化点为边)
由此想到用topsort边遍历边更新点权
输出层的出度为0,因此只要读入时处理一下即可,不用更新
至于阈值,由于他不在sigma中,因此只要是非输入层直接处理
细节坑点见代码

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#define qwq 0
namespace ljm
{
    int dalao;
}
int const maxn=111,maxn2=20110;
int head[maxn],ind[maxn];
int cnt;
int a[maxn],oud[maxn],instack[maxn];
std::queue<int>q;
struct node
{
    int to;
    int next;
    int w;
    node(int to=0,int next=0,int w=0):
        to(to),next(next),w(w){}
}e[maxn2];
void add(int x,int y,int v)
{
    e[++cnt]=(node){y,head[x],v};
    head[x]=cnt;
}
int n,m;
void readin()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        int U;
        scanf("%d%d",&a[i],&U);
        if(!a[i])
            //直接处理阈值
            a[i]-=U;
        else
        {
            q.push(i);
            instack[i]=true;
        }
    }
    for(int x,y,z,i=1;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
        ind[y]++;
        oud[x]++;
    }
}
void topsort()
{
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        instack[u]=false;
        for(int i=head[u];i!=-1;i=e[i].next)
        {
            int v=e[i].to;
            if(a[u]>0)
            {
                //不活跃的点不能传递信息
                a[v]+=a[u]*e[i].w;  
            }
            ind[v]--;
            if(!ind[v]&&!instack[v])
            //instack[]表示队内标记,防止重复入队
            {
                instack[v]=true;
                q.push(v);
            }
        }
    }
}
int main()
{
    memset(head,-1,sizeof(head));
    readin();
    topsort();
    for(int i=1;i<=n;i++)
    {
        if(!oud[i]&&a[i]>0)
            ljm::dalao=true;
    }
    if(!ljm::dalao)
    {
        printf("NULL");
        return 0;
    }
    for(int i=1;i<=n;i++)
        if(!oud[i]&&a[i]>0)
        //这是个超级大坑点
        //输出层(广义上的,指的是出度为0的点)无法传递信息相当于无法输出(就不能算作输出层了)
            printf("%d %d\n",i,a[i]);
    return 0;
}

P1983 车站分级

感觉做图论越来越有感觉了,很清楚自己写了什么,改了一遍就过了,又立flag!
喜闻乐见的语文题,关键在于建图
选中的车站一定大于没选中的,因此如果在其他车次中要选没选中的,选中的一定会被选,这显然跟topsort序有关
由于要求最少等级,我们可以将<=全看作=
举个例子,i1 i2都连向j,可知i1,i2<=j,此时i1==i2肯定是最优的
因此题目就转化成了求最长的topsort链(就是上题 神经网络的“层数lev”)
建图是个难题,细节见代码

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<utility>
int const maxn=1011,maxn2=1011;
struct node
{
    int to,next;
    node(int to=0,int next=0):to(to),next(next){}
}e[maxn*maxn2];
struct node2
{
    int nd;
    int lev;
    node2(int nd=0,int lev=0):
        nd(nd),lev(lev){}
};
int a[maxn][maxn2],vis[maxn][maxn2],ind[maxn],cnt,ans,n,m,head[maxn];
int pan[maxn][maxn];
void add(int u,int v)
{
    e[++cnt]=(node){v,head[u]};
    head[u]=cnt;
}
void readin()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        //共m个车次
        scanf("%d",&a[i][0]);
        //a[i][0]存第i车次的站数
        for(int j=1;j<=a[i][0];j++)
        {
            scanf("%d",&a[i][j]);
            //a[i][j]表示第i车次的第j站
            vis[i][a[i][j]]=true;
        }
        for(int j=a[i][1];j<=a[i][a[i][0]];j++)
            //搜索第i车次起点到终点的所有站点
        {
            if(vis[i][j])
                //只找未被选中的站点
                continue;
            for(int k=1;k<=a[i][0];k++)
                //找选中的站点
                //令j向k连边
            {
                int v=a[i][k];
                if(!pan[j][v])
                {
                    add(j,v);
                    ind[v]++;
                    pan[j][v]=true;
                }
            }
        }
    }
}
void topsort()
{
    std::queue<node2>q;
    for(int i=1;i<=n;i++)
        if(!ind[i])
            q.push(node2(i,1));
            // inque[i]=true;
    while(!q.empty())
    {
        node2 u=q.front();
        q.pop();
        // inque[i]=false;
        for(int i=head[u.nd];i!=-1;i=e[i].next)
        {
            int v=e[i].to;
            ind[v]--;
            if(!ind[v])
            {
                q.push(node2(v,u.lev+1));
                ans=std::max(ans,u.lev+1);
            }
        }
    }
}
int main()
{
    memset(head,-1,sizeof(head));
    readin();
    topsort();
    printf("%d",ans);
    return 0;
}

最小生成树

Prim模板

用前向星存边
把每个点扩展到的边压成点放进堆里
根据优先队列的性质,就能求出集合内到集合外的最小距离
正是因为这个,我们就不用for1-n来求last了…
如果已经在集合内,直接continue,因此无脑压入队列即可

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
int const maxn=5100,maxn2=200100;
int cost[maxn],vis[maxn],cnt,sum,head[maxn],n,m,num;
struct E
{
    int to,next,w;
    E(int to=0,int next=0,int w=0):
        to(to),next(next),w(w){}
}e[maxn2<<1];
struct node
{
    int nd,dis;
    bool operator<(const node &b)const
    {
        return dis>b.dis;
    }
    node(int nd=0,int dis=0):
        nd(nd),dis(dis){}
};
void add(int x,int y,int w)
{
    e[++cnt]=(E){y,head[x],w};
    head[x]=cnt;
}
void readin()
{
    scanf("%d%d",&n,&m);
    for(int u,v,w,i=1;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);
        add(v,u,w);
    }

}
void prim()
{
    std::priority_queue<node>q;
    q.push(node(1,0));
    cost[1]=0;
    while(!q.empty())
    {
        int u=q.top().nd;
        int val=q.top().dis;
        q.pop();
        if(vis[u])
            continue;
        num++;
        sum+=val;
        vis[u]=true;
        for(int i=head[u];i!=-1;i=e[i].next)
        {
            int v=e[i].to;
            if(e[i].w<cost[v])
            //cost[i]表示从集合内到i的最短距离
            {
                cost[v]=e[i].w;
                q.push(node(v,e[i].w));
            }
        }
    }
}
void write()
{
    if(num<0)
    {
        printf("orz");
        return;
    }
    printf("%d",sum);
}
int main()
{
    memset(head,-1,sizeof(head));
    memset(cost,0x7f7f7f7f,sizeof(cost));
    readin();
    prim();
    write();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40828060/article/details/82626675
今日推荐