蜀傳之單刀赴會
題目描述
【題目背景】
公元215年,劉備取益州,孫權令諸葛瑾找劉備索要荊州。劉備不答應,孫權極為惱恨,便派呂蒙率軍取長沙、零陵、桂陽三郡。長沙、桂陽蜀將當即投降。劉備得知後,親自從成都趕到公安(今湖北公安),派大將關羽爭奪三郡。孫權也隨即進駐陸口,派魯肅屯兵益陽,抵擋關羽。雙方劍拔弩張,孫劉聯盟面臨破裂,在這緊要關頭,魯肅為了維護孫劉聯盟,不給曹操可乘之機,決定當面和關羽商談。“肅邀羽相見,各駐兵馬百步上,但諸將軍單刀俱會”。雙方經過會談,緩和了緊張局勢。隨後,孫權與劉備商定平分荊州,“割湘水為界,於是罷軍”,孫劉聯盟因此能繼續維持。
【問題描述】
關羽受魯肅邀請,為了大局,他決定冒險赴會。他帶著侍從周倉,義子關平,騎著赤兔馬,手持青龍偃月刀,從軍營出發了,這就是歷史上赫赫有名的“單刀赴會”。關羽平時因為軍務繁重,決定在這次出行中拜訪幾個多日不見的好朋友。然而局勢緊張,這次出行要在限定時間內完成,關公希望你能夠幫助他安排一下行程,安排一種出行方式,使得從軍營出發,到達魯肅處赴會再回來,同時拜訪到儘可能多的朋友,在滿足這些條件下行程最短。注意拜訪朋友可以在赴會之前,也可以在赴會之後。現在給出地圖,請你完成接下來的任務。
輸入
第一行,代表有個地點,條道路,有個朋友(不包括魯肅),以及限定時間(行走單位長度的路程用時單位時間)。 接下來m行,每行有三個整數,代表和之間有長度為的道路相連。 接下來一行有個整數,代表朋友所在的都城編號(保證兩兩不同,且不在和) (我們約定1是關羽的營地,n是魯肅的營地)
輸出
輸出兩個整數,分別是最多可以拜訪的朋友數,以及在這種情況下最少需要耗費的時間,如果連到達魯肅再回來都無法完成,輸出一個就可以了。
樣例輸入
樣例輸出
提示
【資料規模和約定】 有%資料,; 有%資料,; 有%資料,; 另%資料,; 對於%資料,
解析
首先,我們會發現其實有用點只有個朋友與起點、終點共個點。 而,因此資料範圍急劇下降。 因為要求最短路,我們可以先對這些點以各點為源點做次單源最短路,統計出這些有用點之間的最短路 之後考慮狀壓(那麼小,不狀壓還能咋做?) 表示已經走過的點的集合為,當前在的最少時間 { }
最後統計答案時,優先保證狀態合法(時間要)且的個數儘量多,再考慮路徑最短。 注意:DP出來的結果是不回到起點的,最終統計時要對取
程式碼
#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;
}