1. 程式人生 > >HDU 6071 Lazy Running(同餘最短路)

HDU 6071 Lazy Running(同餘最短路)

簡述題意:給你一個由四個節點組成的環,相鄰兩點間可達,求從節點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)的演算法求出。
 

時間複雜度:O(nmlogn)    m=max(d1,d2)*2

程式碼如下:

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