iPig在假期来到了传说中的魔法猪学院,开始为期两个月的魔法猪训练。通过了一周理论知识和一周基本魔法的学习以后,iPig对猪世界的世界本原有了不少的了解:众所周知,世界是由元素构成的;元素与元素之间能够互相转换;能量守恒……。算法
能量守恒……iPig 今天就在进行一个麻烦的测验。iPig 在以前的学习中已经知道了不少种元素,并学会了能够转化这些元素的魔法,每种魔法须要消耗 iPig 必定的能量。做为 PKU 的顶尖学猪,让 iPig 用最少的能量完成从一种元素转换到另外一种元素……等等,iPig 的魔法导猪可没这么笨!这一次,他给 iPig 带来了不少 1 号元素的样本,要求 iPig 使用学习过的魔法将它们一个个转化为 N 号元素,为了增长难度,要求每份样本的转换过程都不相同。这个看似困难的任务实际上对 iPig 并无挑战性,由于,他有坚实的后盾……如今的你呀!函数
注意,两个元素之间的转化可能有多种魔法,转化是单向的。转化的过程当中,能够转化到一个元素(包括开始元素)屡次,可是一但转化到目标元素,则一份样本的转化过程结束。iPig 的总能量是有限的,因此最多可以转换的样本数必定是一个有限数。具体请参看样例。学习
输入格式:优化
第一行三个数 N、M、E 表示iPig知道的元素个数(元素从 1 到 N 编号)、iPig已经学会的魔法个数和iPig的总能量。spa
后跟 M 行每行三个数 $s_i$、$t_i$、$e_i$ 表示 iPig 知道一种魔法,消耗 $e_i$ 的能量将元素 $s_i$ 变换到元素 $t_i$ 。code
输出格式:blog
一行一个数,表示最多能够完成的方式数。输入数据保证至少能够完成一种方式。队列
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
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。get
数据规模
占总分不小于 10% 的数据知足 $N <= 6$,$M<=15$。
占总分不小于 20% 的数据知足 $N <= 100$,$M<=300$,$E<=100$且$E$和全部的$e_i$均为整数(能够直接做为整型数字读入)。
全部数据知足 $2 <= N <= 5000$,$1 <= M <= 200000$,$1<=E<=10^7$,$1<=e_i<=E$,$E$和$e_i$为实数。
//原本想写一题左偏树练练手速的,百度一下就找到了这题,结果彷佛不必用左偏树。。。只是由于BZOJ很丧病,卡优先队列的内存,你们就纷纷选择了手写堆,某些大牛选择了左偏树当作可持久化堆,动态开点节省空间,而我这等蒟蒻用恒定大小的手写二叉堆的就够了…………
回归正题。
这题很容易看出来是求前k短路,是路径权值之和小于等于E,由于一种转化方式完成后就不能再用了,而为了尽可能多地完成转换,确定要选择当前转换代价最小(最短路、次短路、第3短路……),转换完成一次(路径到达n点一次)就统计答案ans++,最后输出答案。
找k短路的算法最容易想到的是BFS暴搜,第k次搜到n点就是k短路。经过 百度一下 查阅资料咱们学到了一种名叫A*的算法,用它能够求第K短路,证实不会,可是板子挺好背的,看几遍也就记住了……(2019年01月27日 更新 放一个月时间也就忘了)
A*有一个东西叫估价函数$f(n)$,$f(n)=g(n)+h(n)$,在求k短路的问题中,$g(n)$是从起点出发已经走了的长度,$h(n)$是从这个点到终点的最短路。
因为用到每一个点到终点的最短路,咱们能够在反向图上跑一遍spfa,求出终点到每一个点的最短路,用dis[i]表示终点到第$i$号点的最短距离。
而后就能够跑A*了。
创建一个优先队列,每一个元素为{d,u},d=f(u),优先队列是以d为键值的小根堆。
首先把{dis[1],1}/*即第一个点*/加入优先队列,而后进入以下循环——
(1) 从优先队列中取出d最小的节点u;
(2)若是u是终点n,那么就找到了一条k短路(第k次取出n点时的d就是第k短路路径长度),E-=d,若是此时剩余的E>=0(浮点数判断能够用$1e-6$),也就是有足够能量进行这次转换,那么ans++,不然能量不足,退出A*;
(3)拓展正向图中与u直接相连的点,并将它们入队(压入堆中),设当前与u相连的点为v,边权为w,则新的$d=f(v)=f(u)-dis[u]+w+dis[v]$;
(4)若是堆空了,就退出A*,不然返回(1);
————————2018年5月13日更新————————
今天发现,这题在洛谷上被hack了……下面是92分代码,至于AC代码……我留坑吧
————————2019年10月6日更新————————
https://www.luogu.org/blog/cjyl/solution-p2483
原来讲可并堆是由于要可持久化进行优化啊……如今看来下面的代码就是暴力……什么A*嘛
#include<queue> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,m,ans=0; double E; struct Edge{ int next,to; double w; }fe[200010],e[200010];//e存正向图,fe存反向图 int head[200010]={0},cnt=1,fhead[200010]={0},fcnt=1;//带f的都用于存反向图 void add(int u,int v,double w) { e[cnt].to=v; e[cnt].w=w; e[cnt].next=head[u]; head[u]=cnt++;//给正向图加边 fe[fcnt].to=u; fe[fcnt].w=w; fe[fcnt].next=fhead[v]; fhead[v]=fcnt++;//给反向图加边 } double dis[5010];//裸spfa bool inq[5010]={0}; void spfa() { for(int i=0;i<5010;i++) dis[i]=999999999.0; dis[n]=0.0; queue<int> q; q.push(n); inq[n]=1; while(!q.empty()) { int u=q.front(); q.pop(); inq[u]=0; for(int i=fhead[u];i;i=fe[i].next) { int v=fe[i].to; double w=fe[i].w; if(dis[v]>dis[u]+w) { dis[v]=dis[u]+w; if(!inq[v]) { inq[v]=1; q.push(v); } } } } } struct Heap{ double d; int u; bool operator > (const Heap &a)const{ return d>a.d; } }heap[2000010],temp;//手敲优先队列 int sz=1;//优先队列内元素数量+1,我的比较喜欢这种表示方法 void pop()//删除堆顶,取出堆顶直接用heap[1]便可,我没写在pop()里 { sz--; heap[1]=heap[sz]; heap[sz]={0,0}; int the=1,son=2; while(son<sz) { if(heap[son]>heap[son+1]&&son+1<sz) son++; if(heap[the]>heap[son]) swap(heap[the],heap[son]); else break; the=son; son=the<<1; } } void push(double dd,int uu)//加入一个元素,dd=f(uu),dd、uu防止变量名冲突 { heap[sz]={dd,uu}; int the=sz++,fa=the>>1; while(fa) { if(heap[fa]>heap[the]) swap(heap[the],heap[fa]); else break; the=fa; fa>>=1; } } void astar() { push(dis[1],1); while(sz>1) { int u=heap[1].u; double dist=heap[1].d;//取出堆顶 pop();//删除堆顶 if(u==n)//n点出队,说明找到一条k短路 { E-=dist; if(E>=1e-6) ans++; else return; continue; } for(int i=head[u];i;i=e[i].next)//拓展与u相连的节点 { int v=e[i].to; double w=e[i].w; push(dist-dis[u]+w+dis[v],v); } } } int main() { //freopen("test.in","r",stdin); scanf("%d%d%lf",&n,&m,&E); for(int i=1,u,v;i<=m;i++) { double w; scanf("%d%d%lf",&u,&v,&w); add(u,v,w); } spfa(); astar(); printf("%d\n",ans); return 0; }