1. 程式人生 > >蜀傳之單刀赴會

蜀傳之單刀赴會

題目描述

【題目背景】

公元215年,劉備取益州,孫權令諸葛瑾找劉備索要荊州。劉備不答應,孫權極為惱恨,便派呂蒙率軍取長沙、零陵、桂陽三郡。長沙、桂陽蜀將當即投降。劉備得知後,親自從成都趕到公安(今湖北公安),派大將關羽爭奪三郡。孫權也隨即進駐陸口,派魯肅屯兵益陽,抵擋關羽。雙方劍拔弩張,孫劉聯盟面臨破裂,在這緊要關頭,魯肅為了維護孫劉聯盟,不給曹操可乘之機,決定當面和關羽商談。“肅邀羽相見,各駐兵馬百步上,但諸將軍單刀俱會”。雙方經過會談,緩和了緊張局勢。隨後,孫權與劉備商定平分荊州,“割湘水為界,於是罷軍”,孫劉聯盟因此能繼續維持。

【問題描述】

關羽受魯肅邀請,為了大局,他決定冒險赴會。他帶著侍從周倉,義子關平,騎著赤兔馬,手持青龍偃月刀,從軍營出發了,這就是歷史上赫赫有名的“單刀赴會”。關羽平時因為軍務繁重,決定在這次出行中拜訪幾個多日不見的好朋友。然而局勢緊張,這次出行要在限定時間內完成,關公希望你能夠幫助他安排一下行程,安排一種出行方式,使得從軍營出發,到達魯肅處赴會再回來,同時拜訪到儘可能多的朋友,在滿足這些條件下行程最短。注意拜訪朋友可以在赴會之前,也可以在赴會之後。現在給出地圖,請你完成接下來的任務。

輸入

第一行n,m,k,tn,m,k,t,代表有nn個地點,mm條道路,有kk個朋友(不包括魯肅),以及限定時間tt(行走11單位長度的路程用時11單位時間)。 接下來m行,每行有x,y,wx,y,w三個整數,代表xxyy之間有長度為ww的道路相連。 接下來一行有kk個整數,代表朋友所在的都城編號(保證兩兩不同,且不在11nn) (我們約定1是關羽的營地,n是魯肅的營地)

輸出

輸出兩個整數,分別是最多可以拜訪的朋友數,以及在這種情況下最少需要耗費的時間,如果連到達魯肅再回來都無法完成,輸出一個1-1就可以了。

樣例輸入

5

5 77 22 1515 11 22 55 11 33 33 22 33 11 22 44 11 33 44 44 22 55 22 44 55 33 22 44

樣例輸出

22 1414

提示

【資料規模和約定】 有1010%資料,n10,m50,k5n≤10,m≤50,k≤5; 有1010%資料,k=0k=0; 有1010%資料,k=1k=1; 另3030%資料,k5k≤5; 對於100100%資料,n10000,m50000,k15,t2147483647,w10000n≤10000,m≤50000,k≤15,t≤2147483647,w≤10000

解析

首先,我們會發現其實有用點只有kk個朋友與起點、終點共k+2k+2個點。 而k15k≤15,因此資料範圍急劇下降。 因為要求最短路,我們可以先對這些點以各點為源點做k+2k + 2次單源最短路,統計出這些有用點之間的最短路 之後考慮狀壓(kk那麼小,不狀壓還能咋做?) f[sta][i]f[sta][i]表示已經走過的點的集合為stasta,當前在ii的最少時間 f[sta][i]=min(f[sta][j]+dis(i,j))(jsta,stasta,stasta=f[sta][i] = min(f[sta'][j] + dis(i , j))(j∈sta',sta'⊂sta,sta - sta' = { ii } ))

最後統計答案時,優先保證狀態合法(時間要t≤t)且11的個數儘量多,再考慮路徑最短。 注意:DP出來的結果是不回到起點的,最終統計時要對f[sta][i]+dis(i,1)f[sta][i] + dis(i , 1)minmin

程式碼

#include<queue>
#include<stdio.h>
#include<cstring>
#define mp(x , y) make_pair(x , y)
using namespace std;
const int maxk = 20;
const int maxn = 10005;
const int maxe = 50005;
const int oo = 1000000000;
priority_queue < pair < int , int > , vector < pair < int , int > > , greater < pair < int , int > > > Heap;
int d[maxn];
int n;
int edgenum;
int Next[maxe << 1] , vet[maxe << 1] , val[maxe << 1] , head[maxn];
int a[maxk] , D[maxk][maxk];
int f[1 << maxk][maxk] , one_num[1 << maxk];
int min(int x , int y){return x < y ? x : y;}
void add_edge(int u , int v , int cost)
{
	edgenum++;
	Next[edgenum] = head[u];
	vet[edgenum] = v;
	val[edgenum] = cost;
	head[u] = edgenum;
}
void Dijkstra(int s)
{
	for(int i = 1;i <= n;i++) d[i] = oo;
	d[s] = 0;
	Heap.push(mp(d[s] , s));
	while(!Heap.empty())
	{
		int u = Heap.top().second , dis = Heap.top().first;
		Heap.pop();
		if(d[u] != dis) continue;
		for(int e = head[u];e;e = Next[e])
		{
			int v = vet[e];
			if(d[v] > d[u] + val[e]) d[v] = d[u] + val[e] , Heap.push(mp(d[v] , v));
		}
	}
}
int main()
{
	int m , k ,t;
	scanf("%d%d%d%d",&n,&m,&k,&t);
	while(m--)
	{
		int u , v , cost;
		scanf("%d%d%d",&u,&v,&cost);
		add_edge(u , v , cost);
		add_edge(v , u , cost);
	}
	Dijkstra(1);
	if(d[n] + d[n] > t)
	{
		puts("-1");
		return 0;
	}
	a[1] = 1;
	for(int i = 2;i <= k + 1;i++) scanf("%d",&a[i]);
	a[k + 2] = n;
	k += 2;
	for(int i = 1;i <= k;i++)
	{
		Dijkstra(a[i]);
		for(int j = 1;j <= k;j++) D[i][j] = D[j][i] = d[a[j]];
	}
	memset(f , 0x3f3f3f , sizeof f);
	f[1][1] = 0;
	for(int sta = 2;sta < (1 << k);sta++)
	{
		for(int i = 1;i <= k;i++)
		{
			if(!(sta & (1 << i - 1))) continue;
			for(int j = 1;j <= k;j++)
			{
				if(!(sta & (1 << j - 1))) continue;
				f[sta][i] = min(f[sta][i] , f[sta - (1 << i - 1)][j] + D[i][j]);
			}
		}
	}
	one_num[0] = 0;
	for(int i = 1;i < (1 << k);i++) one_num[i] = one_num[i >> 1] + (i & 1);
	int cnt = 2 , ans = oo;
	for(int sta = 0;sta < (1 << k);sta++)
	{
		if(!(sta & (1 << k - 1)) || !(sta & 1)) continue;
		if(one_num[sta] < cnt) continue;
		int res = oo;
		for(int i = 1;i <= k;i++)
		{
			if(!(sta & (1 << i - 1))) continue;
			res = min(res , f[sta][i] + D[i][1]);
		}
		if(res > t) continue;
		if(one_num[sta] == cnt) ans = min(ans , res);
		else if(one_num[sta] > cnt) ans = res , cnt = one_num[sta];
	}
	printf("%d %d\n",cnt - 2 , ans);
	return 0;
}