1. 程式人生 > >【bzoj5180】[Baltic2016]Cities 斯坦納樹

【bzoj5180】[Baltic2016]Cities 斯坦納樹

pan void 斯坦納樹 long 最短路 sdi space push lld

題目描述

給定n個點,m條雙向邊的圖。其中有k個點是重要的。每條邊都有一定的長度。 現在要你選定一些邊來構成一個圖,要使得k個重要的點相互連通,求邊的長度和的最小值。

輸入

共m+2行 第1行:n,k,m,n個點,k個重要的點,m條邊; 第2行共K個點 第3至第m+2行,每行包括3個數字,a,b,c,表示有一條從a到b長度為c的雙向路徑 k<=5 n<=10^5 1<=m<=2*(10^5)

輸出

共1行,即最小長度和

樣例輸入

4 3 6
1 3 4
1 2 4
1 3 9
1 4 6
2 3 2
2 4 5
3 4 8

樣例輸出

11


題解

斯坦納樹裸題

斯坦納樹:給出一些點,選出若幹條邊使得這些點連通,求總邊權的最值。

斯坦納樹是NP問題,不存在多項式時間內的解法,求解方法是狀壓dp。

設 $f[i][j]$ 表示選擇若幹條邊,使得狀態為 $i$ 的給定點連通,並且當前可以選擇下一條邊的端點為 $j$ 的最小邊權和。初始狀態 $f[2^i][pos[i]]=0$ ,其中 $pos[i]$ 為第 $i$ 個給定點的編號。

那麽我們對於每個 $i$ 和 $j$ ,首先枚舉 $i$ 的子集 $k$ ,用 $f[k][j]+f[i-k][j]$ 更新 $f[i][j]$ 。

然後再考慮同層轉移:如果 $x$ 與 $y$ 邊權為 $z$ ,用 $f[i][x]+z$ 更新 $f[i][y]$ ,用 $f[i][y]$ 更新 $f[i][x]$ 。容易發現這個轉移就是最短路,因此使用堆優化Dijkstra跑一遍得出所有的 $f[i][j]$ 。

最終答案就是 $min\{f[2^k-1][i]\}$

時間復雜度 $O(3^k·n+2^k·m\log n)$

本來該是我的一血的 >_<

#include <queue>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define N 100010
using namespace std;
typedef long long ll;
queue<int> q;
int head[N] , to[N << 2] , next[N << 2] , cnt , inq[33][N];
ll len[N << 2] , f[33][N];
inline char nc()
{
	static char buf[100000] , *p1 , *p2;
	return p1 == p2 && (p2 = (p1 = buf) + fread(buf , 1 , 100000 , stdin) , p1 == p2) ? EOF : *p1 ++ ;
}
inline int read()
{
	int ret = 0; char ch = nc();
	while(!isdigit(ch)) ch = nc();
	while(isdigit(ch)) ret = ((ret + (ret << 2)) << 1) + (ch ^ ‘0‘) , ch = nc();
	return ret;
}
inline void add(int x , int y , ll z)
{
	to[++cnt] = y , len[cnt] = z , next[cnt] = head[x] , head[x] = cnt;
}
int main()
{
	int n = read() , p = read() , m = read() , i , j , k , x , y;
	ll z , ans = 1ll << 62;
	memset(f , 0x3f , sizeof(f));
	for(i = 0 ; i < p ; i ++ ) f[1 << i][read()] = 0;
	for(i = 0 ; i < m ; i ++ ) x = read() , y = read() , z = read() , add(x , y , z) , add(y , x , z);
	for(i = 1 ; i < (1 << p) ; i ++ )
	{
		for(j = i ; j ; j = i & (j - 1))
			for(k = 1 ; k <= n ; k ++ )
				f[i][k] = min(f[i][k] , f[j][k] + f[i ^ j][k]);
		for(j = 1 ; j <= n ; j ++ ) inq[i][j] = 1 , q.push(j);
		while(!q.empty())
		{
			x = q.front() , q.pop() , inq[i][x] = 0;
			for(j = head[x] ; j ; j = next[j])
			{
				if(f[i][to[j]] > f[i][x] + len[j])
				{
					f[i][to[j]] = f[i][x] + len[j];
					if(!inq[i][to[j]]) inq[i][to[j]] = 1 , q.push(to[j]);
				}
			}
		}
	}
	for(i = 1 ; i <= n ; i ++ ) ans = min(ans , f[(1 << p) - 1][i]);
	printf("%lld\n" , ans);
	return 0;
}

【bzoj5180】[Baltic2016]Cities 斯坦納樹