牛客练习赛31 C 无畏死灵术士莉莲娜与锁链面纱(dfs + 期望dp)

版权声明:本文为博主原创文章,转载请著名出处 http://blog.csdn.net/u013534123 https://blog.csdn.net/u013534123/article/details/84147484

太久没有做期望/概率dp,已锈。。。

大概就是说给你一个1到n的全排列,然后每次随机选择一个数字在不改变其他数字相对位置的前提下,把比他小的数字放在他前面,大的在后面。问期望几次能够使得这个序列有序。

由于是求期望,所以显然是要倒着求。令dp[x]表示状态x下变得有序的期望步数。那么显然有转移方程:

                                          \large dp[x]=(\frac{t}{n}*dp[x]+\frac{1}{n}*\sum_{x->y}dp[y])+1

其中y表示x可以转移到的状态。也即,x这个状态可以选择下一步走到任意一个可以转移状态也可以还是自己原来的状态不变。这样我们移项把dp[x]都放到左边,那么有转移方程:

                                         \large dp[x]=\frac{n}{n-t}*(1+\frac{1}{n}*\sum_{x->y}dp[y])

那么现在问题的关键就是这个状态如何去确定。看到这里n最多为20,所以我们大胆的猜测,这个总的不重复的状态一定不会很多。我们用dfs暴力出所有的状态,发现当n为20的时候,状态数最多,为5W+,程序也能在规定时间内跑完,完全在我们的承受范围内。在这里,我用了map映射vector的方式去重,但是其实用字符串哈希的话速度更加可观。具体见代码:

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define pi 3.141592653589793
#define mod 998244353
#define LL long long
#define pb push_back
#define lb lower_bound
#define ub upper_bound
#define sf(x) scanf("%d",&x)
#define sc(x,y,z) scanf("%d%d%d",&x,&y,&z)

using namespace std;

const int N = 21;

map<vector<int>,int> mp;
int n,inv[N]; bool v[N];
vector<int> a;

inline void init()
{
    inv[1]=1;
    for(int i=2;i<N;i++)
        inv[i]=(mod-mod/i)*(LL)inv[mod%i]%mod;
}

int dfs(vector<int> a)
{
    if (mp[a]) return mp[a];
    vector<int> b; b.resize(n);
    int ans=0,t=0;
    for(int i=0;i<n;i++)
    {
        if (v[i]) {t++;continue;}
        int l=0,r=i+1;b[i]=i;
        for(int j=0;j<n;j++)
            if (a[j]<i) b[l++]=a[j];
            else if (a[j]>i) b[r++]=a[j];
        v[i]=1;
        if (a==b) t++;
        else ans=(ans+(LL)dfs(b)*inv[n]%mod)%mod;
        v[i]=0;
    }
    return mp[a]=(LL)(ans+1)*n%mod*inv[n-t]%mod;
}

int main()
{
    init(); sf(n);
    a.resize(n);
    for(int i=0;i<n;i++) sf(a[i]),a[i]--;
    printf("%d\n",dfs(a));
    return 0;
}

猜你喜欢

转载自blog.csdn.net/u013534123/article/details/84147484