zoukankan      html  css  js  c++  java
  • 51Nod 1705 七星剑

    一道很新颖概率DP,我看数据范围还以为是有指数级别的复杂度的呢

    记得有人说期望要倒着推,但放在这道题上,就咕咕了吧。

    我们考虑正着概率DP,设(fi)表示将剑升到(i)颗星花费的期望,这样我们可以得出转移:

    • (f_i=f_i+f_{i-1}+c_i) (期望的线性性质,因为无论如何我这(c_i)的代价是一定要花的(无论成功与否))
    • (f_i=f_i+(f_i-f_{i-lose_{i,j}-1})cdot(1-prob_{i,j}))(表示失败降过星之后在通过各种情况(这个之前已经计算过了)再爬上来)

    然后乍一看很成功,但是这个转移有个致命的问题:在转移2中,式子两边同时出现了(f_i)

    这就是传说中的成环DP了,比较通用的方法是利用图论的哲学操作消去这个情况,但我太弱了所以不会

    但在这里有一种说出来吓死你的智障方法——移项

    我们连立两个方程,然后将2中的(f_i imes (1-prob_{i,j}))移过去即可得到:

    (f_i=frac{(f_{i-1}+c_j-(1-prob_{i,j})cdot f_{i-1-lose_{i,j}})}{prob[i][j]})

    然后就可以直接(O(7n))的DP了,这个复杂度是假的吧

    最后注意一下无解的情况要特判

    CODE

    #include<cstdio>
    using namespace std;
    typedef double DB;
    const int N=105;
    const DB EPS=1e-6,INF=1e99;
    int c[N],n,lose[10][N];
    DB p[10][N],f[10],ans;
    bool flag=0;
    inline void miner(DB &x,DB y)
    {
    	if (x>y+EPS) x=y;
    }
    int main()
    {
    	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    	register int i,j; scanf("%d",&n);
    	for (i=1;i<=n;++i) scanf("%d",&c[i]);
    	for (i=1;i<=7;++i)
    	{
    		for (flag=0,j=1;j<=n;++j)
    		scanf("%lf",&p[i][j]),flag|=p[i][j]>EPS;
    		if (!flag) return puts("-1"),0;
    	}
    	for (i=1;i<=7;++i)
    	for (j=1;j<=n;++j)
    	scanf("%d",&lose[i][j]);
    	for (i=1,f[1]=INF;i<=7;++i,f[i]=INF)
    	for (j=1;j<=n;++j)
    	if (p[i][j]>EPS) miner(f[i],(DB)(f[i-1]+c[j]-(1-p[i][j])*f[i-1-lose[i][j]])/p[i][j]);
    	printf("%.9lf",f[7]);
    	return 0;
    }
    
  • 相关阅读:
    C++
    复盘-2018.6.8~2020.6.8
    C++
    C++
    C++
    C++
    Python学习笔记(十)- 面向对象(一)
    SSHException: Error reading SSH protocol banner
    docker 安装mysql
    docker 安装部署
  • 原文地址:https://www.cnblogs.com/cjjsb/p/9344539.html
Copyright © 2011-2022 走看看