一道期望dp的题,推了好久的式子一直算不清楚,最后发现是我对排列数的理解有偏差😥
题面
题目大意:
初始情况为只有n个点的无向图,每一次随机两个点(可能相同),如果这两个点连上边之后满足下列条件就连边,不然图不变。条件:
- 无重边自环
- 每个点的度数小于等于2
不能继续操作就终止,求终止的期望步数
思路
- 可以看出是有重叠的子问题的,在某种情况下开始的期望步数是定的,可以想到用dp
- 每个点的度数小于等于2,那么2度点不能继续连边,0度点不和2度点连边,1度点有一点特殊。考虑1度点的连边情况,有可能1度点和1度点连,这个时候形成长度为2的链,另一种情况是1度点2度点连边,可以发现这两种情况需要分别分析。所以状态比较明显了:
表示当前有 个0度点, 个1度点,这 个1度点中有 条长为2的链,所需要达到终止条件的期望步数 - 状态转移:
- 0度和0度:
- 0度和1度(不在长为2的链上):
//一开始没乘后面的2怎么算也算不对🤡 - 0度和1度(在长为2的链上):
- 1度(不在长为2的链上)和1度(不在长为2的链上):
- 1度(不在长为2的链上)和1度(在长为2的链上):
- 1度(在长为2的链上)和1度(在长为2的链上):
这是所有合法转移,用记忆化搜索的方式写很方便
最终状态
AC代码:
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=205;
double dp[N][N][N];
bool vis[N][N][N];
int n;
double dfs(int i,int j,int k){
if(i<0||j<0||k<0||i+j>n)
return 0;
if(vis[i][j][k])
return dp[i][j][k];
double ans=0;
int sz=i*(i-1)+i*(j-2*k)*2+i*2*k*2+(j-2*k)*(j-2*k-1)+(j-2*k)*2*k*2+2*k*(2*k-2);
if(sz!=0)
+=(dfs(i,j-2,k)*(j-2*k)*(j-2*k-1)+dfs(i,j-2,k-1)*(j-2*k)*2*k*2+dfs(i,j-2,k-2)*2*k*(2*k-2)\
ans+dfs(i-2,j+2,k+1)*i*(i-1)+dfs(i-1,j,k)*i*(j-2*k)*2+dfs(i-1,j,k-1)*i*2*k*2+n*n)/(sz);
[i][j][k]=true;
visreturn dp[i][j][k]=ans;
}
signed main() {
::sync_with_stdio(false);cin.tie(0);
ios>>n;
cin[0][0][0]=true;
vis[0][0][0]=0;
dpdouble ans=dfs(n,0,0);
("%.9lf\n",ans);
printfreturn 0;
}
另一种状态的设计方法
设
状态转移
- 0度和0度:
- 0度和短链:
- 0度和长链:
- 短链和短链:
- 短链和长链:
- 长链和长链(成环):
- 长链和长链(成链):
AC代码
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=205;
double dp[N][N][N];
bool vis[N][N][N];
int n;
double dfs(int i,int j,int k){
if(i<0||j<0||k<0||i+2*j+3*k>n||(k==0&&i+j<=1))
return 0;
if(vis[i][j][k])
return dp[i][j][k];
int sz=i*(i-1)+i*2*j*2+i*2*k*2+2*j*(2*j-2)+2*j*2*k*2+2*k+2*k*(2*k-2);
[i][j][k]=(dfs(i-2,j+1,k)*i*(i-1)+dfs(i-1,j-1,k+1)*i*2*j*2+dfs(i-1,j,k)*i*2*k*2+\
dp(i,j-2,k+1)*2*j*(2*j-2)+dfs(i,j-1,k)*2*j*2*k*2+dfs(i,j,k-1)*2*k+\
dfs(i,j,k-1)*2*k*(2*k-2)+n*n)/sz;
dfs[i][j][k]=true;
visreturn dp[i][j][k];
}
signed main() {
::sync_with_stdio(false);cin.tie(0);
ios>>n;
cin[0][0][0]=true;
vis[0][0][0]=0;
dpdouble ans=dfs(n,0,0);
("%.9lf\n",ans);
printfreturn 0;
}