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; }