51nod 1943 联通期望

版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/qq_35950004/article/details/88641932

在一片大海上有n个岛屿,规划建设 m m 座桥,第i座桥的成本为 z i zi ,但由于海怪的存在,第i座桥有 p i pi 的概率不能建造。
求在让岛屿尽量联通的情况下,期望最小成本为多少。
尽量联通:在对每座桥确定能否建造的情况下,对于任意两个岛屿,如果存在一种建桥方案使得它们联通,那么它们必须联通。
n < = 14 , m < = ( 2 n ) n<=14,m<=\binom 2n

模仿克鲁斯卡尔生成树过程 d p dp 即可,
从小到大加入边,维护每个点集为极大联通块的概率,连边使点集合并时顺便计算答案即可。
复杂度:实现完美且苛刻的 O ( m 3 n 2 ) O(m3^{n-2})
多一点都会T。。。
在这里插入图片描述

可以加入卡精度的优化(值小于eps就不算了)。
大概500ms。

AC Code:

#include<bits/stdc++.h>
#define maxn 14
using namespace std;

int n,m,lg[1<<14];
double p[14][14],f[1<<14],h[1<<14];
struct edge{
	int u,v,z;double p;
	bool operator <(const edge&B)const{ return z==B.z?u==B.u?v<B.v:u<B.u:z<B.z; }
}e[maxn*maxn];

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
		scanf("%d%d%d%lf",&e[i].u,&e[i].v,&e[i].z,&e[i].p),e[i].u--,e[i].v--;
	sort(e+1,e+1+m);
	for(int i=2;i<(1<<n);i++) lg[i]=lg[i>>1]+1;
	for(int i=0;i<(1<<n);i++) h[i]=1;
	int now=1,pre=0;
	double ans = 0 , tmp;
	for(int i=0;i<n;i++) f[1<<i]=1;
	for(int i=1;i<=m;i++,swap(now,pre)){
		register int su=1<<e[i].u,sv=1<<e[i].v,suv=su|sv,sa=((1<<n)-1)^su^sv,rsa;
		for(int j=sa;;j=(j-1)&sa)if(f[j|su]){
			rsa = sa^j;
			for(int k=rsa;;k=(k-1)&rsa)if(f[k|sv]){
				f[suv|j|k] += (tmp = f[j|su]*f[k|sv]*(1-e[i].p)/(h[j|k|suv]/h[j|su]/h[k|sv]));
				ans += tmp*e[i].z;
 				if(!k) break;
			}
			if(!j) break;
		}
		for(int j=sa;;j=(j-1)&sa){
			f[su|j] *= e[i].p;
			f[sv|j] *= e[i].p;
			h[j|suv] *= e[i].p;
			if(!j) break;
		}
	}
	printf("%.6lf\n",ans);
} 

注意到计算答案时,可以不用子集合并,直接前i条边联通-前 i 1 i-1 条边联通,可以做到统计答案 O ( 2 n ) O(2^n)
大佬(讲题人)说联通概率可以,子集和卷积?然后 O ( 2 n ) O(2^n) ,然后可以把 n n 开大,反正我。。。推不出来。急,离线等

猜你喜欢

转载自blog.csdn.net/qq_35950004/article/details/88641932