概率DP A1295 necklace

试题来源
  清华大学2011年百名信息学优秀高中学子夏令营
问题描述
  有人打算送给你一条宝石项链,包含了N颗五颜六色(一共有M种颜色)的宝石。因为本问题中你只关心每个宝石的颜色,而且项链现在两头还没有接在一起,它可以被看成是一个数字串。
  你希望在五颜六色的宝石中看到连续的一段同色宝石。因此,你定义一根宝石项链的幸运度是它最长的由同色宝石构成的连续子串的长度。 比如,项链112322211的幸运度是3,因为它包括了由同色宝石构成的子串222。而首尾的两个11并不构成连续1111,因为这个项链现在是串形的而不是环形的。
  然而,你还没有见到这个项链。你只知道每个宝石是每种颜色的概率。并且,已知每个宝石的颜色分布是独立的。现在你希望在真的见到这条项链之前计算一下,这条项链的幸运度的期望是多少?
输入格式
  输入的第一行有两个正整数N和M。
  后面N行每行有M个非负实数。其中第i行第j列的数P_(i,j)含义是第i个宝石是颜色j的概率是P_(i,j)。每行的M个实数保证和为1。
输出格式
  一个实数,即这条项链的幸运度的期望。四舍五入至小数点后6位。
样例输入
4 2
1.0 0.0
0.5 0.5
0.0 1.0
0.5 0.5
样例输出
2.250000
样例说明
  我们用1和2来分别表示两种颜色的宝石,则这串项链有四种等概率的情形:1121,1122,1221和1222。它们的幸运度分别是2,2,2,3,因此期望的幸运度是2.25。
数据规模和约定
  30%的数据满足N≤16,M≤3。
  60%的数据满足N≤100。
  100%的数据满足2≤N≤1000,2≤M≤10。

设 p[i][j]是第j个数在第i位的概率。
设 f[i][j][k]表示到第i位,最长长度为j,末位为k的概率。
设 g[i][j][k]表示从i位到第j位连续为k的概率
设 s[i][j]表示第i位最长长度为j概率之和。

我们转移时只要考虑会不会产生长度为j+1的连续k就好了(其他不合法情况已经排除了)也就是说不合法情况只有i-j~i位上都是k.而i-j-1位上不是。
那么怎么算不合法情况很明显了:(s[i-j-1][j]-f[i-j-1][j][k])*g[i-j][j][k]
转移方程就是 f[i][j][k]=s[i-1][j]*a[i][k]-(s[i-j-1][j]-f[i-j-1][j][k])*g[i-j][j][k]

注意:初始化s[0][0~n]都是1,而且j要循环到n,虽然当前f[i][j][k]不会有贡献,但后面做减法时可能会用到,但是用前缀和s减的,所以j要一直更新到n

#pragma GCC optimize("O3")
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define N 100005
using namespace std;
int n,m;
double a[11][1005],s[1010][1010],g[11][1010][1010],f[12][1100][1100],ans;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%lf",&a[j][i]);
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
        {
            g[i][j][j]=a[i][j];
            for(int k=j+1;k<=n;k++)
                g[i][j][k]=g[i][j][k-1]*a[i][k];
        }
    s[0][0]=1;
    for(int i=1;i<=n;i++)s[0][i]=1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            for(int k=1;k<=m;k++)
            {
                f[k][i][j]=s[i-1][j]*a[k][i];
                if(i-j-1>=0)f[k][i][j]-=(s[i-j-1][j]-f[k][i-j-1][j])*g[k][i-j][i];
                s[i][j]+=f[k][i][j];
            }
    for(int i=1;i<=n;i++)
        ans+=(double)(s[n][i]-s[n][i-1])*i;
    printf("%.5lf",ans);
}
原文地址:https://www.cnblogs.com/QTY2001/p/7643082.html