【题解】k短路-【SDOI-2010-魔法猪学院】

一周前同机房大佬们考了一套图论题,其中第一题就是k短路模板,一直没去看,昨天才翻出来

题目描述

iPig在假期来到了传说中的魔法猪学院,开始为期两个月的魔法猪训练。经过了一周理论知识和一周基本魔法的学习之后,iPig对猪世界的世界本原有了很多的了解:众所周知,世界是由元素构成的;元素与元素之间可以互相转换;能量守恒……。

能量守恒……iPig 今天就在进行一个麻烦的测验。iPig 在之前的学习中已经知道了很多种元素,并学会了可以转化这些元素的魔法,每种魔法需要消耗 iPig 一定的能量。作为 PKU 的顶尖学猪,让 iPig 用最少的能量完成从一种元素转换到另一种元素……等等,iPig 的魔法导猪可没这么笨!这一次,他给 iPig 带来了很多 1 号元素的样本,要求 iPig 使用学习过的魔法将它们一个个转化为 N 号元素,为了增加难度,要求每份样本的转换过程都不相同。这个看似困难的任务实际上对 iPig 并没有挑战性,因为,他有坚实的后盾……现在的你呀!

注意,两个元素之间的转化可能有多种魔法,转化是单向的。转化的过程中,可以转化到一个元素(包括开始元素)多次,但是一但转化到目标元素,则一份样本的转化过程结束。iPig 的总能量是有限的,所以最多能够转换的样本数一定是一个有限数。具体请参看样例。

输入输出格式

输入格式:

第一行三个数 N、M、E 表示iPig知道的元素个数(元素从 1 到 N 编号)、iPig已经学会的魔法个数和iPig的总能量。

后跟 M 行每行三个数 si、ti、ei 表示 iPig 知道一种魔法,消耗 ei 的能量将元素 si 变换到元素 ti 。

输出格式:

一行一个数,表示最多可以完成的方式数。输入数据保证至少可以完成一种方式。

输入输出样例

输入样例#1:

4 6 14.9
1 2 1.5
2 1 1.5
1 3 3
2 3 1.5
3 4 1.5
1 4 1.5

输出样例#1:

3

说明

有意义的转换方式共4种:

1->4,消耗能量 1.5
1->2->1->4,消耗能量 4.5
1->3->4,消耗能量 4.5
1->2->3->4,消耗能量 4.5

显然最多只能完成其中的3种转换方式(选第一种方式,后三种方式仍选两个),即最多可以转换3份样本。 如果将 E=14.9 改为 E=15,则可以完成以上全部方式,答案变为 4。

数据规模

占总分不小于 10% 的数据满足 N <= 6,M<=15。

占总分不小于 20% 的数据满足 N <= 100,M<=300,E<=100且E和所有的ei均为整数(可以直接作为整型数字读入)。

所有数据满足 2 <= N <= 5000,1 <= M <= 200000,1<=E<=107,1<=ei<=E,E和所有的ei为实数。

题目大意

给出一张有向图,包括经过每一条边的花费,主人公有e点能量,求这些能量能供主人公从S到T走多少次(要求每一次走的路径均不同,且走过一遍之后能量不会恢复)

实际上当时我是打算找一道k短路裸题(POJ-2449)练练手的,但是今早一来发现vjudge提交失败,打开POJ显示

1026 网 络 维 护 至 10 月 26 号 !
心“唰”的一下就凉了,只好找一道POJ之外的题练手,遂发现这题

我怎么也没想到这题在洛谷上是 NOI/NOI+/CTSC 的难度

后来发现它卡常厉害

思路

就如标题所说,这题要求实现最多的方案数,且能量不恢复,一眼就看出肯定是要选最小的路径,再想一想,这不就是k短路吗,假设答案是ans,那么选最小的ans条总没错,那不就是要求最小的k条路径吗

于是乎这题就变成了k短路的裸题:每找到一条最短路径,就将e减去该最短路径长度,当e小于当前最短路径长度时输出当前是第几短路

接下来就要求k短路了,然而k短路使用A*算法,由于加上绝对正确的估价函数后,该算法弹出的路径一定是严格递增的,我们只要保证估价函数绝对正确

那么反向SPFA可以帮我们快速地解决这个问题(建议别用dijkstra堆优化,打俩堆的感觉真爽

最后,当终点每出一次队,就是一条k短路(k++)

写了这么久还被洛谷卡常,怒怼

被卡常的代码

#include<queue>
using namespace std;
#define get(x) (zhi[x]+fdis[num[x]])
#define inf 2000000005
#define eps 1e-10

template <typename _Tp> inline void read(_Tp &x){
    char c11=getchar();x=0;bool booo=0;
    while(c11<'0'||c11>'9'){if(c11=='-')booo=1;c11=getchar();}
    while(c11>='0'&&c11<='9'){x=x*10+c11-'0';c11=getchar();}
    if(booo)x=-x;
    return ;
}

const int maxn=5005,maxm=200050;
int s,e,k,n,m;double tot;
struct node {int v,next;double w;}a[maxm];
struct node1{int v,next;double w;}a1[maxm];
int head1[maxn],p1=0;
int head[maxn],p=0;
int A,B;
double C;
int num[maxn<<10],top=0,pp;
double zhi[maxn<<10];
double fdis[maxn];

void init();
inline void add(int,int,double);
inline void add1(int,int,double);

inline void push(int A,double B){
    num[++top]=A;zhi[top]=B;
    pp=top;
    while(pp>1&&get(pp>>1)>get(pp)){
        swap(zhi[pp>>1],zhi[pp]);
        swap(num[pp>>1],num[pp]);
        pp>>=1;
    }
    return ;
}

inline void pop(){
    if(!top)return ;
    num[1]=num[top];
    zhi[1]=zhi[top--];
    pp=1;
    while( ( ( pp << 1 ) <= top && get ( pp << 1 ) < get ( pp ) ) || ( ( pp << 1 | 1 ) <= top && get ( pp << 1 | 1 ) < get ( pp ) ) ) {
        pp<<=1;
        if(get(pp)>get(pp|1))
            pp|=1;
        swap(zhi[pp],zhi[pp>>1]);
        swap(num[pp],num[pp>>1]);
    }
    return ;
}
int x=num[1];double w=zhi[1],vv;
void work(){
    push(s,0);
    int anx=0;
    while(top){
        x=num[1];w=zhi[1];
        if(w+fdis[x]>tot){
            printf("%d",anx);
            return ;
        }
        pop();
/*      if(x==e){
            anx++;
            tot-=w+fdis[x];
            continue;
        }*/
        for(register int i=head[x];i;i=a[i].next){
            vv=a[i].v;
            if(vv==e){
                anx++;
                tot-=w+a[i].w+fdis[a[i].v];
                continue;
            }
            push(vv,w+a[i].w);
        }
        //if(top>(maxn<<8))break;
    }
    printf("%d",anx);
    return ;
}
queue <int> q;
void spfa(){
    for(register int i=1;i<=n;i++)fdis[i]=inf;
    fdis[e]=0.0;
    q.push(e);
    while(!q.empty()){
        //if(q.size()>1000000)return;
        int x=q.front();
        q.pop();
        for(register int i=head1[x];i;i=a1[i].next){
            int v=a1[i].v;double w=a1[i].w;
            if(fdis[v]-fdis[x]-w>-eps){
                fdis[v]=(double)fdis[x]+w;
                q.push(v);
            }
        }
    }
}

int main(){
    init();
    spfa();
    work();
    return 0;
}

void init(){
    read(n);read(m);scanf("%lf",&tot);
    for(register int i=0;i<m;i++){
        read(A);read(B);scanf("%lf",&C);
        add(A,B,C);
        add1(B,A,C);
    }
    s=1;e=n;
}

inline void add1(int u,int v,double w){
    a1[++p1].v=v;
    a1[p1].w=w;
    a1[p1].next=head1[u];
    head1[u]=p1;
    return ;
}

inline void add(int u,int v,double w){
    a[++p].v=v;
    a[p].w=w;
    a[p].next=head[u];
    head[u]=p;
    return ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值