问题
https://vjudge.net/problem/UVA-11600
分析
概率dp,看到了N<=30,想到了状态压缩,然后由于N比较大,而且状态中并不是所有状态都存在,所以使用map存储状态
对于M条边的处理,有两种办法,一种是用并查集,将连接起来的边连成一个集合,每次到下一个城市的时候,都去查看这个点的父节点和对应的 二进制的值(对应这个集合的所有点)
参考:https://www.cnblogs.com/jerryRey/p/4857941.html
或者将所有的联通点看作一个点,给这个点赋权,因为是多个点连接起来的,所以被访问的概率会比单独的点要大
状态转移方程 很明显 S是已走到的状态,k是从开始点能够联通的点的个数
d(S)代表现在状态是S,要全部联通的时间期望值
d
(
S
)
=
k
/
(
n
−
1
)
∗
d
(
S
)
+
1
/
(
n
−
1
)
∗
∑
j
d
(
S
∣
(
1
<
<
j
)
)
d(S)=k/(n-1)*d(S)+1/(n-1)*\sum_jd(S|(1<<j))
d(S)=k/(n−1)∗d(S)+1/(n−1)∗∑jd(S∣(1<<j)) 其中j不属于S
并查集:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <map>
#include <string>
#include <vector>
#include <algorithm>
#include <queue>
#include <unordered_map>
using namespace std;
typedef long long LL;
const int maxn=30+5;
int n,m,T,a,b,fa[maxn],digit[maxn];
unordered_map<int,double> dp;
void init(){
for(int i=0;i<n;++i){
fa[i]=i;
digit[i]=1<<i;
}
}
inline int findFa(int x){
//路径压缩
return (x==fa[x])?x:fa[x]=findFa(fa[x]);
}
inline void unit(int x,int y){
fa[x]=y;
digit[y]|=digit[x];
}
double dfs(int s){
auto iter=dp.find(s);
if(iter!=dp.end()) return iter->second;
int k=0;
double temp=0;
for(int i=1;i<n;++i){
if(!(s&(1<<i))){
//digit[fa[i]]是i的联通集合
temp+=dfs(s|digit[fa[i]]);
}else ++k;
}
return dp[s]=(temp+n-1)/(n-1-k);
}
int main(void){
scanf("%d",&T);
for(int t=1;t<=T;++t){
scanf("%d%d",&n,&m);
dp.clear();
init();
for(int i=0;i<m;++i){
scanf("%d%d",&a,&b);
int x=findFa(--a),y=findFa(--b);
if(x!=y) unit(x,y);
}
//方便查询,这一步一定要和下面的dfs(digit[0])添加
for(int i=0;i<n;++i) digit[i]=digit[findFa(i)];
dp[(1<<n)-1]=0;
//digit[0],确定初始化的集合
double ans=dfs(digit[0]);
printf("Case %d: %.7lf\n",t,ans);
}
return 0;
}
合并点,加权重:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <map>
#include <string>
#include <vector>
#include <algorithm>
#include <queue>
#include <unordered_map>
#include <vector>
using namespace std;
typedef long long LL;
const int maxn=30+5;
int n,m,T,a,b,nn,weight[maxn],vis[maxn];
unordered_map<int,double> dp;
vector<int> g[maxn];
void init(){
for(int i=0;i<=n;++i){
g[i].clear();
}
memset(vis,0,sizeof(vis));
memset(weight,0,sizeof(weight));
}
int dfs_cnt(int u){
int temp=1;
vis[u]=1;
for(int i=0;i<g[u].size();++i){
if(vis[g[u][i]]) continue;
temp+=dfs_cnt(g[u][i]);
}
return temp;
}
double dfs(int s){
auto iter=dp.find(s);
if(iter!=dp.end()) return iter->second;
int k=0;
double temp=0;
for(int i=0;i<nn;++i){
if(!(s&(1<<i))){
//digit[fa[i]]是i的联通集合
temp+=weight[i]*dfs(s|(1<<i));
}else k+=weight[i];
}
return dp[s]=(temp+n-1)/(n-k);
}
int main(void){
scanf("%d",&T);
for(int t=1;t<=T;++t){
scanf("%d%d",&n,&m);
dp.clear();
init();
for(int i=0;i<m;++i){
scanf("%d%d",&a,&b);
--a; --b;
g[a].push_back(b);
g[b].push_back(a);
}
nn=0;
for(int i=0;i<n;++i){
if(!vis[i]){
weight[nn++]=dfs_cnt(i);
}
}
dp[(1<<nn)-1]=0;
double ans=dfs(1);
printf("Case %d: %.7lf\n",t,ans);
}
return 0;
}