51Nod 1705 七星劍

一道很新穎機率DP,我看數據範圍還覺得是有指數級別的複雜度的呢spa

記得有人說指望要倒着推,但放在這道題上,就咕咕了吧。code

咱們考慮正着機率DP,設\(fi\)表示將劍升到\(i\)顆星花費的指望,這樣咱們能夠得出轉移:io

  • \(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\)class

這就是傳說中的成環DP了,比較通用的方法是利用圖論的哲學操做消去這個狀況,但我太弱了因此不會方法

但在這裏有一種說出來嚇死你的智障方法——移項im

咱們連立兩個方程,而後將2中的\(f_i\times (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]}\)di

而後就能夠直接\(O(7n)\)的DP了,這個複雜度是假的吧co

最後注意一下無解的狀況要特判time

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;
}
相關文章
相關標籤/搜索