牛客假期训练三:处女座的比赛资格(拓扑排序求有负边的最短路径)

题目链接:传送门

思路:因为存在负值边,所以一开始就想广搜求出最短路径,结果错了,后来发现是没用好bfs。

题解上说可以用拓扑排序求带负权值边的最短路径,这次get到了。

就是邻接表的使用还不太熟悉,代码是参考排名第一的大佬的。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn = 2e5+10;
const int INF = 0x3f;
int u[maxn],v[maxn],c[maxn],ai[maxn],bi[maxn];
int head[maxn],rd[maxn],tot,m,n;
LL dis[maxn];
struct Node{
    int id,nxt;
    LL data;
}edge[maxn];
void Init()
{
    tot=1;
    memset(rd,0,sizeof(rd));
    memset(head,-1,sizeof(head));
    memset(dis,INF,sizeof(dis));
}
Node tmp;
void addedge(int x,int y,LL dd)
{
    tmp.id=y;tmp.data=dd;tmp.nxt=head[x];
    edge[tot]=tmp;
    head[x]=tot++;
    rd[y]++;
} 
LL bfs()
{
    queue <int> q;
    int i,j;
    dis[1]=0;
    for(i=1;i<=n;i++)
    if(rd[i]==0) q.push(i);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(i=head[u];i!=-1;i=edge[i].nxt)
        {
            int v=edge[i].id;
            if(dis[v]>dis[u]+edge[i].data) dis[v]=dis[u]+edge[i].data;
            rd[v]--;
            if(rd[v]==0) q.push(v);
        }
    }
    return dis[n];
}
LL MAX(LL x,LL y)
{
    return x>y?x:y;
}
int main(void)
{
    int T,i,j;
    LL ans;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        for(i=0;i<m;i++) scanf("%d%d%d%d%d",&u[i],&v[i],&c[i],&ai[i],&bi[i]);
        ans=0;
        Init();
        for(i=0;i<m;i++) addedge(u[i],v[i],(LL)(c[i]-bi[i]));
        ans+=MAX(0ll,bfs());
        
        Init();
        for(i=0;i<m;i++) addedge(u[i],v[i],(LL)(c[i]-ai[i]));
        ans-=MAX(0ll,bfs());
        if(ans>0) printf("cnznb!!!
%lld
",ans);
        else if(ans==0) printf("oof!!!
");
        else printf("rip!!!
%lld
",-ans);
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/2018zxy/p/10324978.html