UVALive8138 - Number Generator 概率dp

版权声明:转载请注明出处 https://blog.csdn.net/jay__bryant/article/details/82154591

题意:数字生成器随机生成n种数(1~n),(1<=n<=3000),已经成了k个数,再给出已经生成的k个数。求使得1~n都至少出现2次,还需要生成次数的期望。有t组数据(1<=t<=1e5)

朴素做法,dp[i][j]:已经由i个数出现了1次,j个数出现了2次,达到目标状态需要的期望。
复杂度O(n*n*t),tle。
船新思路,将状态设置为与n无关,则只需要一遍n*n就可以t输出了,复杂度O(n*n+t);
dp[i][j]:已经由n-i个数出现了1次,n-j个数出现了2次,达到目标状态需要的期望。
转移方程:
这里写图片描述

只有dp[i][j]前面的系数是(1-x)的形式才可以提n!!!

#include <cstdio>
#include <cstring>

using namespace std;
const int N = 3010;
bool vis1[N],vis2[N];
int cnt1,cnt2;
double dp[N][N];
int n, k;

void init()
{
    memset(dp, 0, sizeof(dp));
    for(int i = 0; i <= 3000; ++i)
        for(int j = i; j <= 3000; ++j)
        {
            if(i==0 && j==0) continue;
            dp[i][j] = 1.0/j;
            if(i!=0) dp[i][j] += 1.0*i/j*dp[i-1][j];
            if(j!=0) dp[i][j] += 1.0*(j-i)/j*dp[i][j-1];
        }
}

int main()
{
    init();
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d %d", &n, &k);
        memset(vis1, 0, sizeof(vis1));
        memset(vis2, 0, sizeof(vis2));
        cnt1=cnt2=0;
        for(int i = 1; i <= k; ++i)
        {
            int x;
            scanf("%d", &x);
            if(!vis1[x])
            {
                vis1[x] = 1;
                ++cnt1;
            }
            else if(!vis2[x])
            {
                vis2[x] = 1;
                ++cnt2;
            }
        }
        printf("%.9f\n", n*dp[n-cnt1][n-cnt2]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/jay__bryant/article/details/82154591