​vj传送门​

首先需要预处理每个状态哪些点可以遍历完图

这个过程可以记忆化搜索

临界点是状态为 ( 1 < < n ) − 1 (1<<n)-1 (1<<n)−1,包含所有点,此时无论在哪个点都遍历完全图了

由此可以开始 d f s dfs dfs,能到达这个状态的也能遍历全图…

这样预处理后, d p dp dp就变得方便很多了

再来一遍 d f s dfs dfs去 d p dp dp即可

#include <bits/stdc++.h>
using namespace std;
const int maxn=(1<<15)+10;
double dp[maxn][16],cnt[maxn][16];
int t,n,m,mx,mp[maxn][16];
struct edge{
int to,nxt; double w;
}d[maxn]; int head[maxn],k=1;
void add(int u,int v,double w){
d[++k]=(edge){v,head[u],w},head[u]=k;
}
int dfs(int u,int sta)
{
if( mp[sta][u]!=-1 ) return mp[sta][u];
if( sta==mx ) return mp[sta][u]=1;
cnt[sta][u]=mp[sta][u]=0;
for(int i=head[u];i;i=d[i].nxt )
{
int v=d[i].to;
if( (sta&(1<<v))||!dfs(v,sta|(1<<v)) ) continue;
mp[sta][u]=1; cnt[sta][u]++;
}
return mp[sta][u];
}
double DP(int u,int sta)
{
if( dp[sta][u]!=-1 ) return dp[sta][u];
if( sta==mx||cnt[sta][u]==0 ) return dp[sta][u]=0;
double p=1.0/(cnt[sta][u]+1);
dp[sta][u] = 5.0*p;
for(int i=head[u];i;i=d[i].nxt )
{
int v=d[i].to;
if( (sta&(1<<v))||!mp[sta|(1<<v)][v] ) continue;
dp[sta][u]+=( DP(v,sta|(1<<v))+d[i].w )*p;
}
dp[sta][u]/=(1-p);
return dp[sta][u];
}
int main()
{
cin >> t;
int casenum=0;
while( t-- )
{
cin >> n >> m;
mx = (1<<n)-1;
for(int i=0;i<=mx;i++)
for(int j=0;j<n;j++)
mp[i][j]=dp[i][j]=-1,cnt[i][j]=0;
for(int i=1;i<=m;i++)
{
int l,r; double w; cin >> l >> r >> w;
add(l,r,w); add(r,l,w);
}
dfs(0,1);
printf("Case %d: %.7lf\n",++casenum,DP(0,1) );
k=1;
for(int i=0;i<=n;i++) head[i]=0;
}
}