1. 程式人生 > >【BZOJ1417】Pku3156 Interconnect 記憶化搜索

【BZOJ1417】Pku3156 Interconnect 記憶化搜索

str -s cpp ios cto highlight 期望 scan hash

【BZOJ1417】Pku3156 Interconnect

Description

給出無向圖G(V, E). 每次操作任意加一條非自環的邊(u, v), 每條邊的選擇是等概率的. 問使得G連通的期望操作次數. (|V| <= 30, |E| <= 1000)

Input

第一行兩個整數N,M 1<=N<=30 0<=M<=1000 接下來M行,每行兩個整數X,Y表示兩者之間已修好一條道路. 兩點之間可以不止修了一條路,也有可能M條路已使N個點成為一個整體.

Output

輸出一個小數,表示新修道路條數的期望值,保留六位小數.

Sample Input

4 2
1 2
3 4

Sample Output

1.500000

題解:思路非常巧妙。

因為30的整數劃分只有幾千種,所以我們可以將每個連通塊的大小設為狀態,排序後用vector+map存起來,然後進行DP(其實應該用hash的,交到poj上TLE了)。

DP時枚舉連接的是哪兩個連通塊,也有可能連接的是同一個連通塊,簡單推一推算一算就好。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
typedef vector<int> vec;
map<vec,double> g;
int n,m;
int f[35],siz[35];
double DP(vec v)
{
	if(v.size()==1)	return 0;
	if(g.find(v)!=g.end())	return g[v];
	vec::iterator i,j,k;
	double gv=0,tmp=n*(n-1)/2;
	for(i=v.begin();i!=v.end();i++)	tmp-=(*i)*((*i)-1)/2;
	for(i=v.begin();i!=v.end();i++)
	{
		for(j=i,j++;j!=v.end();j++)
		{
			vec t;
			for(k=v.begin();k!=v.end();k++)	if(k!=i&&k!=j)	t.push_back(*k);
			t.push_back((*i)+(*j));
			sort(t.begin(),t.end());
			gv+=DP(t)*(*i)*(*j)/tmp;
		}
	}
	return g[v]=n*(n-1)/2/tmp+gv;
}
int find(int x)
{
	return (f[x]==x)?x:(f[x]=find(f[x]));
}
int main()
{
	scanf("%d%d",&n,&m);
	int i,a,b;
	for(i=1;i<=n;i++)	f[i]=i,siz[i]=1;
	for(i=1;i<=m;i++)
	{
		scanf("%d%d",&a,&b),a=find(a),b=find(b);
		if(a!=b)	siz[b]+=siz[a],f[a]=b;
	}
	vec v;
	for(i=1;i<=n;i++)	if(find(i)==i)	v.push_back(siz[i]);
	sort(v.begin(),v.end());
	printf("%.6lf",DP(v));
	return 0;
}

【BZOJ1417】Pku3156 Interconnect 記憶化搜索