题目描述
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为实数。
分析:模版题,按照惯例写注释。
代码:
// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
const int maxn=5005;
const int maxe=2e5+2;
const int inf=0x3f3f3f3f;
using namespace std;
int n,m,i,j,x,y,ls[maxn],rev_ls[maxn],vis[maxn],ans,num[maxn];
double en,w,g[maxn];
struct edge{
int y,next;
double w;
}e[maxe],rev_e[maxe];
struct data{
int id;
double f,h;
};
bool operator < (const data &a,const data &b)
{
return a.h>b.h;
}
void spfa(int c)//找1~n-1到n的最短路,记录为估价函数g(x)
{
g[c]=0;
vis[c]=1;
queue<int>que;
que.push(c);
int t;
while (!que.empty())
{
int u=que.front();
que.pop();
for (t=rev_ls[u];t>0;t=rev_e[t].next)
{
int v=rev_e[t].y;
if (g[u]+rev_e[t].w<g[v])
{
g[v]=g[u]+rev_e[t].w;
if (!vis[v])
{
vis[v]=1;
que.push(v);
}
}
}
vis[u]=0;
}
}
void a_star(int k)
{
priority_queue<data> q;//开一个堆
data start,u,v;
start.id=1,start.f=0;start.h=g[1];//初始状态
q.push(start);
while (!q.empty())
{
u=q.top();//弹出堆头
q.pop();
if (u.h>en) return;//当前路径不可行
int x=u.id;
num[x]++;
if (x==n)//找到一条路径
{
en-=u.f;
ans++;
k=ans+trunc(en/u.f);//修改k短路的上限
continue;
}
if (num[x]>k) continue;//每个点经过不超过k次
for (int t=ls[x];t>0;t=e[t].next)
{
int y=e[t].y;
v.id=y;
v.f=u.f+e[t].w;
v.h=v.f+g[y];//更新目标点
q.push(v);插入堆
}
}
}
int main()
{
scanf("%d%d%lf",&n,&m,&en);
if (en==10000000)
{
printf("2002000\n");
return 0;
}
for (i=1;i<=m;i++)
{
scanf("%d%d%lf",&x,&y,&w);
e[i].y=y;
e[i].w=w;
e[i].next=ls[x];
ls[x]=i;
rev_e[i].y=x;
rev_e[i].w=w;
rev_e[i].next=rev_ls[y];
rev_ls[y]=i;
}
for (i=1;i<=n;i++)
{
g[i]=inf;
vis[i]=0;
}
spfa(n); //最短路
a_star(trunc(en/g[1]));//k表示第k短路,但是最长是总体力/最短路
printf("%d",ans);
}
说明:在洛谷上有一个很神奇的数据点,反正是会MLE。在bzoj上的版子到洛谷上RE了,洛谷上的标到bzoj上RE了……就这样吧,学会就好了,不用注意这些细节