zoukankan      html  css  js  c++  java
  • 「HNOI 2015」亚瑟王

    (Description)

    (n)张卡牌,每一张卡牌有(p_i)的概率发动,并造成(d_i)点伤害.一共有(r)轮,每一轮按照编号从小到大依次考虑,如果这张牌已经发动过则跳过该牌,否则以(p_i)的概率发动,如果发动成功则造成伤害然后结束该轮,否则跳过这张牌.问期望造成的伤害,(T)组询问

    (n<=220,r<=132,T<=444)

    (Solution)

    这道的答案怎么算应该挺好想的吧.

    [sum_{i=1}^n dp[i]*d[i] ]

    (dp[i]) 表示第i张牌出现的概率.

    但是现在问题就是怎么算(dp)数组啊?

    (dp[1])显然啊.为:

    [1-(1-p[1])^r ]

    ((1-p[1])^r)为一直不出的概率.

    但是现在好像貌似只能看出来(dp[1])(QAQ)

    接续上(dp)了,我们令(f[i][j])为在(r)轮中,前(i)张卡中一共出了(j)张的概率,

    至于转移方程,还需要分类讨论一下:

    (Case 1:)

    (f[i][j])(f[i-1][j-1])转移

    这表示选了第(i)张牌,现在在(r)轮中有(j-1)轮选的是(i)之前的牌,而(i)没有被选到,所以(i)被选到的轮数为(:r-j+1)

    转移方程为:

    [f[i][j]=f[i-1][j-1]*(1-(1-p[i])^{r-j+1} ]

    $ Case 2:( )f[i][j](由)f[i-1][j]$转移而来.

    表示不选第i张牌

    那么在(r)轮中已经过了(r-j)轮了,剩下的不选(i)的概率为((1-p[i])^{r-j})

    所以转移方程为:

    [f[i][j]=f[i-1][j]*(1-p[i])^{r-j} ]

    现在算出来了(f)数组,接下来就要来算(dp)数组了

    [dp[i]=f[i-1][j]*(1-(1-p[i])^{r-j} ]

    (Code)

    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    #include<cstring>
    #define rg register
    #define file(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout);
    using namespace std;
    int read(){
        int x=0,f=1;
        char c=getchar();
        while(c<'0'||c>'9') f=(c=='-')?-1:1,c=getchar();
        while(c>='0'&&c<='9') x=x*10+c-48,c=getchar();
        return f*x;
    }
    int d[1001],r,n;
    double f[1001][1001],power[1001][1001],p[1001],dp[1001];
    void init(){
    	memset(f,0,sizeof(f));
    	memset(dp,0,sizeof(dp));
    	n=read(),r=read();
    	for(int i=1;i<=n;i++)
    		scanf("%lf",&p[i]),d[i]=read();
    	for(int i=1;i<=n;i++){
    		power[i][0]=1;
    		for(int j=1;j<=r;j++)
    			power[i][j]=power[i][j-1]*(1-p[i]);
    	}
    }
    void solve(){
        init();
    	f[1][0]=power[1][r],f[1][1]=dp[1]=1.0-f[1][0];
    	for(int i=2;i<=n;i++)
    		for(int j=0;j<=min(i,r);j++){
    			if(j)
    				f[i][j]+=f[i-1][j-1]*(1.0-power[i][r-j+1]);
    			if(i!=j) f[i][j]+=f[i-1][j]*power[i][r-j];
    		}
    	for(int i=2;i<=n;i++)
            for(int j=0;j<=min(i-1,r);j++)
    			dp[i]+=f[i-1][j]*(1-power[i][r-j]);
    	double ans=0;
    	for(int i=1;i<=n;i++)
    		ans+=dp[i]*d[i];
    	printf("%0.10lf
    ",ans);
    }
    int main(){
    	int T=read();
    	while(T--)
    		solve();
    }
    
  • 相关阅读:
    mirco新建proto流程
    Ubuntu默认防火墙安装、启用、配置、端口、查看状态相关信息
    Rails核心组件
    Ruby中文乱码问题
    python str转dict
    SQLserver AwaysOn日志文件过大,处理办法
    MySQL的一些小细节
    mysql删除表中重复值
    可恶的自增长标识符
    reset slave all更彻底
  • 原文地址:https://www.cnblogs.com/hbxblog/p/10413908.html
Copyright © 2011-2022 走看看