[ZJOI2015]地震后的幻想乡

题目传送门

SOL:不会积分的我瑟瑟发抖。

所以我选择状压DP。

 我们有以下一个dp状态:

     f[S][i],S表示点集,i表示这个点集向外联了i条边。

    那么答案就是f[(1<<n)-1][0]了,那么让我们来考虑怎么转移。

。。。我口胡不下去了。

我们来考虑数学方法。

我们设答案是 EX,那么我们有以下数学推导:

(不会打数学公式,只好手写再拍照上传了)

点这里(数学推导第一张)

点这里(数学推导第二张)

 就酱紫。

我的代码丑,给你们看看大佬的代码:

#include<bits/stdc++.h>
#define N 11
#define M 57
#define sight(c) ('0'<=c&&c<='9')
int n,m,x,y,lim;
bool vis[1<<N][M];int T[1<<N][1<<N];double _f[1<<N][M];
inline void read(int &x){
    static char c;
    for (c=getchar();!sight(c);c=getchar());
    for (x=0;sight(c);c=getchar()) x=x*10+c-48;
}
double f(int S, int t) {
  if (S==1) return 0;
  if (vis[S][t]) return _f[S][t];
  vis[S][t]=true;
  double &ans=(_f[S][t]=.0);
  for (int S2=(S-1)&S; S2^S; S2=(S2-1)&S) if (S2&1) {
    ans+=1.0/(t+T[S2][S&~S2]+1);
    ans-=f(S2,t+T[S2][S&~S2]);
  }
  return ans;
}
int main() {
  read(n); read(m); lim=1<<n;
  while (m--) {
    read(x); read(y);x--,y--;
    for (int S1=0;S1<lim;++S1) if ((S1>>x) & 1)
      for (int S2=0;S2<lim;++S2) if ((S2>>y) & 1)
        ++T[S2][S1],++T[S1][S2];
  }
  printf("%.6lf
", f(lim-1,0));
  return 0;
}
原文地址:https://www.cnblogs.com/rrsb/p/8149523.html