1. 程式人生 > >51nod 1705 七星劍(期望DP)

51nod 1705 七星劍(期望DP)

Description

夾克村附近來了一個大魔王,為了保護村民們的安全,夾老爺選出勇士準備去消滅這個大魔王。為了提高勇士的戰鬥力,夾克老爺決定出資為這個勇士打造一把神兵——七星劍。要打造一把七星劍,得在劍上鑲嵌7顆魔法石,在夾克村中一共找到N種不同的魔法石,標號為1,2,3..,N,每種魔法石都有很多個,其中,第i種魔法石售價為C(i)夾克幣。打造七星劍需要將魔法石一顆一顆的煉化上去,每成功煉化一次稱為加了一顆星,但由於煉化過程十分看中機緣,所以不是每一次煉化都能成功。根據古書裡記載在加第k顆星的時候(1<=k<=7),使用不同的魔法石會有不同的成功機率,書中給出了一些統計資料,大概是說在煉化第k顆星時,用魔法石i將有prob(k,i)的機率成功,即煉化後劍上從原有的(k-1)顆星變成k顆星,但是如果失敗不但不會多出星來還會丟失lose(k,i)顆星(0<=lose(k,i)<=k-1),當然這次使用的魔法石也會被毀壞。因為魔法石比較昂貴,夾克老爺希望儘可能少的花費夾克幣來打造七星劍。問夾克老爺打造七星劍花費的期望的最小值是多少夾克幣?(相對於昂貴的魔法石,我們忽略所有鑄劍與煉化過程的花費,只考慮花在魔法石上的費用)

解釋一下樣例:
一共有2種魔法石,每一次煉化失敗都會降為0顆星,但發現煉化過程有一種100%成功的方法,即依次使用魔法石{1,1,2,2,1,1,2}即可,總花費為10.除了這種方案,其他方案期望都比10大。

Input

一組測試資料.
第一行會有一個整數N,表示魔法石的種類有多少種,其中,1<=N<=100.
之後一行會有N個整數,第i個數表示魔法石i的價格Ci,其中1<=Ci<=10000.
之後7行記錄prob矩陣,這7行中每行N個小數,第k行的第i項表示prob(k,i)的大小,其中0<=prob(k,i)<=1,且每一項小數點後最多2位。
再之後的7行記錄了lose矩陣,這7行中每行N個小數,第k行的第i項表示lose(k,i)的大小,其中0<=lose(k,i)<=k-1。

Output

每組詢問輸出一行一個小數,表示夾克老爺的最小期望花費(絕對誤差或相對誤差在1e-8範圍內即可,並不要求輸出多少位,只要精度對即可),如果夾克老爺永遠沒法鑄造出七星劍,那麼輸出-1.
(友情提示:程式碼需要注意精度問題)

Input示例

2
1 2
1.0 0.1
1.0 0.1
0.1 1.0
0.1 1.0
1.0 0.1
1.0 0.1
0.1 1.0
0 0
1 1
2 2
3 3
4 4
5 5
6 6

Output示例

10.00

解題思路

ans[i]代表新增第i顆魔法石時的期望,則有
ans[i]=ans[i-1]+c[i][j]+(1-p[i][j])*(ans[i]-ans[i-1-l[i][j]])
化簡可得
ans[i]=(ans[i-1]-ans[i-1-l[i][j]]*(1-p[i][j]))/p[i][j].
如果對於某一行k的所有prob[k][i]均為0,則需特判-1。

程式碼實現

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const double inf=1e100;
#define maxn 107
bool flag;
int l[8][maxn];
double p[8][maxn],ans[8],c[maxn];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%lf",&c[i]);
    for(int i=1;i<=7;i++)
    {
        double flag=0.0;
        for(int j=1;j<=n;j++)
        {
            scanf("%lf",&p[i][j]);
            flag+=abs(p[i][j]);
        }
        if(flag<1e-8)
        {
            printf("-1\n");
            return 0;
        }
    }
    for(int i=1;i<=7;i++)
        for(int j=1;j<=n;j++)
        scanf("%d",&l[i][j]);
    for(int i=1;i<=7;i++)
        ans[i]=inf;
    for(int i=1;i<=7;i++)
        for(int j=1;j<=n;j++)
    {
        if(p[i][j])
            ans[i]=min(ans[i],(ans[i-1]+c[j]-ans[i-1-l[i][j]]*(1.0-p[i][j]))/p[i][j]);
    }
    printf("%.8f\n",ans[7]);
    return 0;
}