处女座的比赛资格(拓扑排序)

链接:https://ac.nowcoder.com/acm/contest/329/B
来源:牛客网
 

题目描述

 

处女座想出去比赛,但是又不知道学校能不能给到足够的经费。然而处女座是大众粉丝,有着很好的人缘,于是他找了一个在学校管经费的地方勤工俭学偷来了一份报销标准。


由于处女座是万人迷,所以他在中间途径的每一条线路上都会发生一些故事,也许是粉丝给他发了一个200元的微信红包,也许是和他的迷妹一起吃饭花了500元。


而经费负责人也实地考察了每一条路线,在每一条路上,也许是天降红包雨,也许是地生劫匪。每一条路上都有属于自己的奇遇。


而经费负责人也只能根据他的故事决定这一路批下来多少经费。他会找出从宁波到比赛地的最小花费,并以此作为标准给处女座打比赛。而处女座也会选择对他来说最小花费的路线,来节省使用。
处女座想知道,最终的经费是否够用,如果够还会剩下来多少钱。如果不够,他自己要自费掏出多少钱。(当然处女座和经费管理人都具有旅途中无限信贷额度,所有收入支出会在旅行结束后一起结算。)
 

输入描述:

输入文件第一行包含一个整数T,表示处女座要参加的比赛场数。

对于每一场比赛,第一行包含两个整数N,M,分别表示旅行中的站点数(其中宁波的编号为1,比赛地的编号为N)和线路数。

接下来M行,每一行包含5个整数u,v,c,cnz,jffzr,分别表示从u到v有一条单向的线路,这条线路的票价为c。处女座搭乘这条线路的时候,会得到cnz元(如果为负即为失去-cnz元);经费负责人搭乘这条线路的时候,会得到jffzr元(如果为负即为失去-jffzr元)。

行程保证不会形成环,并保证一定能从宁波到达比赛地。

输出描述:

对于每一场比赛,如果经费负责人给出的经费绰绰有余,则先在一行输出"cnznb!!!",并在下一行输出他可以余下的经费;如果处女座的经费不够用,则先在一行输出"rip!!!",并在下一行输出他需要自费的金额;如果经费负责人给出的经费正好够处女座用,则输出一行"oof!!!"。(所有输出不含引号)

示例1

输入

复制

1
3 3
1 2 300 600 -600
2 3 100 -300 1
1 3 200 0 0

输出

复制

cnznb!!!
100

说明

处女座先走第一条路再走第二条路到达,总花费100元,经费负责人走第三条路,花费200元,处女座经费剩余100元

备注:

T≤10T≤10

2≤N≤10^5 2≤N≤10^5

1≤M≤2⋅10^5 1≤M≤2⋅10^5
1≤u,v≤N1≤u,v≤N
0≤c≤10^9 0≤c≤10^9
−10^9≤cnz,jffzr≤10^9

 

思路:

这个题目是求两个有负权值的最短路问题。这个题目的数据量用一般的最短路算法肯定不行。

因为题目中说了。这个图是有向无环图。所以如果进行拓扑排序的话,那么所有点都会进队列一遍。所以,我们可以进行一遍拓扑排序,在排序的过程中求一遍最短路。

这个题求的是最小费用,所以可能会很绕。

这二个题有些坑点。那么当求得两个最短路如果是负数,不是赚钱,而是费用为0.

 

代码:

#include <bits/stdc++.h>

using namespace std;
#define ll long long
const int maxn = 2e5+100;
const int maxm = 1e5 + 100;
const ll INF=1e16;
int n, m;
int in[maxn];
ll dis[maxn];
struct Edge
{
	int to, next,va;
	Edge() {}
	Edge(int t,int n,int v)
	{
		to = t;
		next = n;
		va=v;
	}
}edges[maxn];
int head[maxn], tot;

void add_edge(int from, int to,int v)
{
	edges[tot] = Edge ( to, head[from],v);
	head[from] = tot++;
	in[to]++;
}

bool topo()
{
    queue<int> Q;
    int sum=0;
    for(int i=1;i<=n;i++)if(in[i]==0)
    Q.push(1);
    dis[1]=0;
    while(!Q.empty())
    {
        int u=Q.front(); Q.pop();
        sum++;
        for(int e=head[u];e!=-1;e=edges[e].next)
        {
            int v=edges[e].to;
            dis[v]=min(dis[v],dis[u]+edges[e].va);
            if(--in[v]==0)
            {

                Q.push(v);
            }
        }
    }
    return sum==n;
}


void init()
{
	memset(in, 0, sizeof(in));
	for(int i=0;i<=n;i++)dis[i]=INF;
	memset(head, -1, sizeof(head));
	tot = 0;
}
struct node1
{
    int u,v,w;
}w[maxn*2];

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        memset(w,0,sizeof(w));
        scanf("%d%d",&n,&m);
        init();
        for(int i=1;i<=m;i++)
        {
            int u,v,c,w1,w2;
            scanf("%d%d%d%d%d",&u,&v,&c,&w1,&w2);
            add_edge(u,v,c-w1);
            w[i].u=u;w[i].v=v;w[i].w=c-w2;
        }
        topo();
        ll sum=dis[n];
        sum=max(0LL,sum);

        init();
        for(int i=1;i<=m;i++)
        {
            add_edge(w[i].u,w[i].v,w[i].w);
        }
        topo();
        ll sum_1=dis[n];
        sum_1=max(sum_1,0LL);

        ll ans1=sum_1-sum;
        if(ans1>0)
        {
            printf("cnznb!!!\n%lld\n",ans1);
        }
        else if(ans1==0)
        {
            printf("oof!!!\n");
        }
        else
        {
            printf("rip!!!\n%lld\n",-ans1);
        }

    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值