版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/qq_35950004/article/details/88641932
在一片大海上有n个岛屿,规划建设
座桥,第i座桥的成本为
,但由于海怪的存在,第i座桥有
的概率不能建造。
求在让岛屿尽量联通的情况下,期望最小成本为多少。
尽量联通:在对每座桥确定能否建造的情况下,对于任意两个岛屿,如果存在一种建桥方案使得它们联通,那么它们必须联通。
模仿克鲁斯卡尔生成树过程
即可,
从小到大加入边,维护每个点集为极大联通块的概率,连边使点集合并时顺便计算答案即可。
复杂度:实现完美且苛刻的
多一点都会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条边联通-前
条边联通,可以做到统计答案
大佬(讲题人)说联通概率可以,子集和卷积?然后
,然后可以把
开大,反正我。。。推不出来。急,离线等。