处女座的比赛资格

【题目描述】

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

由于处女座是万人迷,所以他在中间途径的每一条线路上都会发生一些故事,也许是粉丝给他发了一个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

思路:

本题实质上是在一有向无环图上求最短路,但数据量的范围使 Floyd 会超时,由于存在负边权,因此 Dijkstra 不适用,而由于 DAG 图的特性,使得 SPFA 无法在有限时间内求出。

考虑 DAG 的特性,因此可按照原图的拓扑顺序依次递推距离求解,令 dis[i] 为结点 1 到 i 的最短路,则有 dis[i]=mid(dis[j]+cost(j+i)),其中 j 为 i 的前驱结点,cost(j,i) 为结点 j 到 i 的有向边的边权

【源代码】

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#define PI acos(-1.0)
#define E 1e-6
#define MOD 1000000007
#define INF 0x3f3f3f3f
#define N 100001
#define LL long long
using namespace std;
struct Edge{
    int to,next;
    LL c,cnz,jffer;
}edge[N*2];
int n,m;
int cnt,head[N];
int deg[N],tmp[N];//度数
LL res[N];
void addEdge(int to,int next,LL c,LL cnz,LL jffzr){
    ++cnt;
    edge[cnt].to=to;
    edge[cnt].c=c;;
    edge[cnt].cnz=cnz;
    edge[cnt].jffer=jffzr;
    edge[cnt].next=head[next];
    head[next]=cnt;
}
LL DAG(int S){
    queue<int> Q;
    memset(res,INF,sizeof(res));
    res[1]=0;
    
    for(int i=1;i<=n;i++){
        tmp[i]=deg[i];
        if(tmp[i]==0)
            Q.push(i);
    }    

    while(!Q.empty()){
        int x=Q.front();
        Q.pop();
        for(int i=head[x];i!=-1;i=edge[i].next){
            Edge &e=edge[i];
            LL temp;

            if(S==0)
                temp=e.c-e.cnz;
            else
                temp=e.c-e.jffer;

            res[e.to]=min(res[e.to],res[x]+temp);

            if(--tmp[e.to]==0)
                Q.push(e.to);
        }
    }
    return res[n];
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);

        memset(deg,0,sizeof(deg));
        memset(head,-1,sizeof(head));
        cnt=0;

        for(int i=1;i<=m;i++){
            int u,v;
            LL c,cnz,jffzr;
            scanf("%d%d%lld%lld%lld",&u,&v,&c,&cnz,&jffzr);
            addEdge(v,u,c,cnz,jffzr);
            deg[v]++;
        }

        LL res1=max(0LL,DAG(0));
        LL res2=max(0LL,DAG(1));

        if(res1==res2)
            puts("oof!!!");
        else if(res1>res2)
            printf("rip!!!\n%lld\n",res1-res2);
        else
            printf("cnznb!!!\n%lld\n",res2-res1);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值