HGOI 20181027 幻象(概率DP)

  • 40 pts: 考场上打了40分暴力,理论的话就是概率树,把每一个状态去去到各个带权(概率)的和就是答案

最终处理的话就是dfs出01序列0代表没有幻象,1代表出现幻象然后在每一次dfs出一段序列的时候双指针check一下更新答案

代码并不难,就是这样写的复杂度O(2n

code: (40pts

# include <bits/stdc++.h>
using namespace std;
double ans;
int n,a[105];
double p1[105],p0[105];
void get()
{
    double pr=1.0;
    for (int i=1;i<=n;i++)
     if (a[i]==1) pr*=p1[i];
     else pr*=p0[i];
    int i=1; double sum=0;
    while (i<=n) {
        if (a[i]==0) { i++; continue;}
        int j=i; int res=0; 
        while (a[j]==1&&j<=n) j++,res++;
        i=j;
        sum=sum+(double) res*res;
    } 
    ans+=sum*pr;
}
void dfs(int dep)
{
    if (dep==n+1) { get(); return; }
    a[dep]=1; dfs(dep+1);
    a[dep]=0; dfs(dep+1);
}
int main()
{
    freopen("phantom.in","r",stdin);
    freopen("phantom.out","w",stdout);
    scanf("%d",&n);
    int x;
    for (int i=1;i<=n;i++) {
        scanf("%d",&x);
        p1[i]=(double) x/100.0;
        p0[i]=1.0-p1[i];
    }
    dfs(1);
    printf("%.1lf\n",ans);
    return 0;
}
  • 60pts:hjc20032003考场上写了60分DP,这大概是O(n2)[前缀积]或者是O(n3)复杂度

具体做法是这样的:

令f[i]表示第i秒前幻象的期望,枚举是从j转移过来的,(这里需要注意枚举的j是断点所以从0开始枚举表示不断,否则会重复计数)

转移方程式这样的:

code:(60pts)

# include <bits/stdc++.h>
using namespace std;
const int MAXN=105;
double f[MAXN],p[MAXN];
int n;
int main()
{
    scanf("%d",&n); int x;
    for (int i=1;i<=n;i++) {
        scanf("%d",&x);
        p[i]=(double)x/100.0;
    } 
    f[1]=p[1];
    for (int i=2;i<=n;i++) {
        f[i]=0.0;
        for (int j=0;j<=i;j++) {
            double pr=1.0;
            for (int k=j+1;k<=i;k++) pr*=p[k];
            f[i]+=pr*(1-p[j])*(f[j-1]+(i-j)*(i-j));
        }
    }
    printf("%.1lf\n",f[n]);
    return 0;
 }
  • 100pts: 状态改变一下,

记f[i]为第i秒之前期望幻象值

记g[i]为第i秒前连续期望幻象值

显然

    • g[i]=(g[i-1]+1)*P[i]
    • f[i]=f[i-1]+P[i]*((g[i-1]+1)2-g[i-1]2)

于是完成了O(1)转移,总复杂度O(n)

code(100 pts) 

# include <bits/stdc++.h>
using namespace std;
const int MAXN=1e6+10;
double p[MAXN],f[MAXN],g[MAXN];
int n;
double sqr(double x){ return x*x;}
int main()
{
    scanf("%d",&n);
    int x;
    for (int i=1;i<=n;i++) {
        scanf("%d",&x);
        p[i]=(double) x/100.0;
    }
    f[1]=p[1]; g[1]=p[1];
    for (int i=2;i<=n;i++) {
        f[i]=f[i-1]+p[i]*(sqr(g[i-1]+1)-sqr(g[i-1]));
        g[i]=(g[i-1]+1)*p[i];
    } 
    printf("%.1lf\n",f[n]);
    return 0;
}

 

posted @ 2018-10-27 16:29  ljc20020730  阅读(184)  评论(0编辑  收藏  举报