2022 上海市赛SHCPC I题

一道期望dp的题,推了好久的式子一直算不清楚,最后发现是我对排列数的理解有偏差😥


题面

题目大意:
初始情况为只有n个点的无向图,每一次随机两个点(可能相同),如果这两个点连上边之后满足下列条件就连边,不然图不变。条件:
- 无重边自环
- 每个点的度数小于等于2

不能继续操作就终止,求终止的期望步数


思路

  • 可以看出是有重叠的子问题的,在某种情况下开始的期望步数是定的,可以想到用dp
  • 每个点的度数小于等于2,那么2度点不能继续连边,0度点不和2度点连边,1度点有一点特殊。考虑1度点的连边情况,有可能1度点和1度点连,这个时候形成长度为2的链,另一种情况是1度点2度点连边,可以发现这两种情况需要分别分析。所以状态比较明显了: 表示当前有 个0度点, 个1度点,这 个1度点中有 条长为2的链,所需要达到终止条件的期望步数
  • 状态转移:
  1. 0度和0度:
  2. 0度和1度(不在长为2的链上): //一开始没乘后面的2怎么算也算不对🤡
  3. 0度和1度(在长为2的链上):
  4. 1度(不在长为2的链上)和1度(不在长为2的链上):
  5. 1度(不在长为2的链上)和1度(在长为2的链上):
  6. 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)
        ans+=(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)\
        +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);
    vis[i][j][k]=true;
    return dp[i][j][k]=ans;
}
signed main() {
    ios::sync_with_stdio(false);cin.tie(0);
    cin>>n;
    vis[0][0][0]=true;
    dp[0][0][0]=0;
    double ans=dfs(n,0,0);
    printf("%.9lf\n",ans);
    return 0;
}

另一种状态的设计方法

为现在有 个0度点, 条长为2的链, 条长大于2的链,要达到最终状态所需要的期望步数

状态转移
  • 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);
    dp[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+\
    dfs(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;
    vis[i][j][k]=true;
    return dp[i][j][k];
}
signed main() {
    ios::sync_with_stdio(false);cin.tie(0);
    cin>>n;
    vis[0][0][0]=true;
    dp[0][0][0]=0;
    double ans=dfs(n,0,0);
    printf("%.9lf\n",ans);
    return 0;
}

img_show