HDU 6071 Lazy Running(同餘最短路)
阿新 • • 發佈:2018-11-09
簡述題意:給你一個由四個節點組成的環,相鄰兩點間可達,求從節點2出發,回到節點2的不小於k的最短路徑的長度。
演算法:同餘最短路
難度:NOIP
題解:
假設我們將任意一條長度大於k的迴路(從2出發回到2)為可行路徑,那麼任意一條可行路徑加上2w一定還是可行路徑,所有可行方案中,最短的是k,最長的為 k + 2 * n * w,(n趨近正無窮),(因為我們可以無限迴圈地走)顯然我們不可能求出所有的可行路徑來,由於所有可行路徑長度中都含有i*2w(i=0,1,2...),我們可以考慮按對2w的餘數分塊(列舉),這樣我們只要求出%2w == 0的最短可行路徑,%2w == 1的最短可行路......一直到%2w == 2w - 1的最短可行路徑,然後在這2w條可行路徑中找一條最短的就是答案了(因為這條一定是所有可行路徑中最短的一條)
然後問題就是找到求出%2w同餘的所有可行路徑中最短的那條了,用dis[i][j]表示從2號點出發到達i,長度%2w為j的最短路徑的長度(dis儲存的不一定是合法方案,長度可以小於K,因為我們可以最後加x個2w使其剛好大於等於K,並且新增2w以後並不會改變其長度%2w == j 的性質),這個陣列可以用dijkstra(spfa)(dijspfa)的演算法求出。
時間複雜度:
程式碼如下:
#include <cstdio> #include <iostream> #include <cstring> #include <cmath> #include <cstdlib> #include <queue> #include <algorithm> #define ll long long #define N 15 using namespace std; ll k; struct node { int next; int to; int val; }edg[N<<1]; int hea[N]; ll dis[5][60005]; int cnt=1; void init() { memset(hea,-1,sizeof(hea)); cnt=1; } void add(int u,int v,int w) { edg[cnt].next=hea[u]; edg[cnt].to=v; edg[cnt].val=w; hea[u]=cnt++; } struct nod { int po; ll d; }; bool operator < (nod x,nod y) { return x.d>y.d; } int mm; priority_queue<nod>Q; void dijspfa(int rt) { memset(dis,0x3f3f3f3f,sizeof(dis)); nod tem; tem.po=rt; tem.d=0ll; Q.push(tem); while(!Q.empty()) { nod op; op=Q.top(); Q.pop(); int u=op.po; ll w=op.d; if(w>dis[u][w%mm]) continue; for(int i = hea[u];i != -1;i=edg[i].next) { int to=edg[i].to; ll ww=w+edg[i].val; if(dis[to][ww%mm]>ww) { dis[to][ww%mm]=ww; nod ee; ee.po=to; ee.d=ww; Q.push(ee); } } } } int main() { int T,d1,d2,d3,d4; scanf("%d",&T); while(T--) { init(); scanf("%I64d%d%d%d%d",&k,&d1,&d2,&d3,&d4); mm=max(d1,d2)*2; add(1,2,d1),add(2,1,d1); add(2,3,d2),add(3,2,d2); add(3,4,d3),add(4,3,d3); add(4,1,d4),add(1,4,d4); dijspfa(2); ll ans=0x7fffffffffffffffll; for(int i = 0;i < mm;i++) { ll dta=k-dis[2][i]; if (dta<=0) ans=min(ans,dis[2][i]); else ans=min(ans,dis[2][i]+dta/mm*mm+(dta%mm>0)/*如果為成立,就是1,反之則是0*/*mm); } printf("%I64d\n",ans); } return 0 ; }