1. 程式人生 > >P1850 換教室 概率dp

P1850 換教室 概率dp

ostream 道路 char scan 意義 小數 turn 順序 轉移

其實說是概率dp,本質上和dp沒什麽區別,就是把所有可能轉移的情況全枚舉一下就行了,不過dp方程確實有點長。。。

ps:這個題的floyed我竟然之前寫跪了。。。

題目:

技術分享圖片
題目描述

對於剛上大學的牛牛來說,他面臨的第一個問題是如何根據實際情況申請合適的課程。

在可以選擇的課程中,有 2n2n2n 節課程安排在 nnn 個時間段上。在第 iii(1≤i≤n1 \leq i \leq n1≤i≤n)個時間段上,兩節內容相同的課程同時在不同的地點進行,其中,牛牛預先被安排在教室 cic_ici? 上課,而另一節課程在教室 did_idi? 進行。

在不提交任何申請的情況下,學生們需要按時間段的順序依次完成所有的 nnn 節安排好的課程。如果學生想更換第 iii 節課程的教室,則需要提出申請。若申請通過,學生就可以在第 iii 個時間段去教室 did_idi? 上課,否則仍然在教室 cic_ici? 上課。

由於更換教室的需求太多,申請不一定能獲得通過。通過計算,牛牛發現申請更換第 iii 節課程的教室時,申請被通過的概率是一個已知的實數 kik_iki?,並且對於不同課程的申請,被通過的概率是互相獨立的。

學校規定,所有的申請只能在學期開始前一次性提交,並且每個人只能選擇至多 mmm 節課程進行申請。這意味著牛牛必須一次性決定是否申請更換每節課的教室,而不能根據某些課程的申請結果來決定其他課程是否申請;牛牛可以申請自己最希望更換教室的 mmm 門課程,也可以不用完這 mmm 個申請的機會,甚至可以一門課程都不申請。

因為不同的課程可能會被安排在不同的教室進行,所以牛牛需要利用課間時間從一間教室趕到另一間教室。

牛牛所在的大學有 vvv 個教室,有 eee 條道路。每條道路連接兩間教室,並且是可以雙向通行的。由於道路的長度和擁堵程度不同,通過不同的道路耗費的體力可能會有所不同。 當第 iii(
1≤i≤n−11 \leq i \leq n-11≤i≤n−1)節課結束後,牛牛就會從這節課的教室出發,選擇一條耗費體力最少的路徑前往下一節課的教室。 現在牛牛想知道,申請哪幾門課程可以使他因在教室間移動耗費的體力值的總和的期望值最小,請你幫他求出這個最小值。 輸入輸出格式 輸入格式: 第一行四個整數 n,m,v,en,m,v,en,m,v,e。nnn 表示這個學期內的時間段的數量;mmm 表示牛牛最多可以申請更換多少節課程的教室;vvv 表示牛牛學校裏教室的數量;eee表示牛牛的學校裏道路的數量。 第二行 nnn 個正整數,第 iii(
1≤i≤n1 \leq i \leq n1≤i≤n)個正整數表示 cic_ici?,即第 iii 個時間段牛牛被安排上課的教室;保證 1≤ci≤v1 \le c_i \le v1≤ci?≤v。 第三行 nnn 個正整數,第 iii(1≤i≤n1 \leq i \leq n1≤i≤n)個正整數表示 did_idi?,即第 iii 個時間段另一間上同樣課程的教室;保證 1≤di≤v1 \le d_i \le v1≤di?≤v。 第四行 nnn 個實數,第 iii(1≤i≤n1 \leq i \leq n1≤i≤n)個實數表示 kik_iki?,即牛牛申請在第 iii 個時間段更換教室獲得通過的概率。保證 0
≤ki≤10 \le k_i \le 10≤ki?≤1。 接下來 eee 行,每行三個正整數 aj,bj,wja_j, b_j, w_jaj?,bj?,wj?,表示有一條雙向道路連接教室 aj,bja_j, b_jaj?,bj?,通過這條道路需要耗費的體力值是 wjw_jwj?;保證 1≤aj,bj≤v1 \le a_j, b_j \le v1≤aj?,bj?≤v, 1≤wj≤1001 \le w_j \le 1001≤wj?≤100。 保證 1≤n≤20001 \leq n \leq 20001≤n≤20000≤m≤20000 \leq m \leq 20000≤m≤20001≤v≤3001 \leq v \leq 3001≤v≤3000≤e≤900000 \leq e \leq 900000≤e≤90000。 保證通過學校裏的道路,從任何一間教室出發,都能到達其他所有的教室。 保證輸入的實數最多包含 333 位小數。 輸出格式: 輸出一行,包含一個實數,四舍五入精確到小數點後恰好222位,表示答案。你的輸出必須和標準輸出完全一樣才算正確。 測試數據保證四舍五入後的答案和準確答案的差的絕對值不大於 4×1034 \times 10^{-3}4×103。 (如果你不知道什麽是浮點誤差,這段話可以理解為:對於大多數的算法,你可以正常地使用浮點數類型而不用對它進行特殊的處理) 輸入輸出樣例 輸入樣例#1: 復制 3 2 3 3 2 1 2 1 2 1 0.8 0.2 0.5 1 2 5 1 3 3 2 3 1 輸出樣例#1: 復制 2.80 說明 【樣例1說明】 所有可行的申請方案和期望收益如下表: 【提示】 道路中可能會有多條雙向道路連接相同的兩間教室。 也有可能有道路兩端連接的是同一間教室。 請註意區分n,m,v,e的意義, n不是教室的數量, m不是道路的數量。 特殊性質1:圖上任意兩點 aia_iai?, bib_ibi?, aia_iai?≠ bib_ibi?間,存在一條耗費體力最少的路徑只包含一條道路。 特殊性質2:對於所有的 1≤i≤n1≤ i≤ n1≤i≤n, ki=1k_i= 1ki?=1
題目-

代碼:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
#define duke(i,a,n) for(int i = a;i <= n;i++)
#define lv(i,a,n) for(int i = a;i >= n;i--)
#define clean(a) memset(a,0,sizeof(a))
const int INF = 1e9 + 7;
typedef long long ll;
typedef double db;
template <class T>
void read(T &x)
{
    char c;
    bool op = 0;
    while(c = getchar(), c < 0 || c > 9)
        if(c == -) op = 1;
    x = c - 0;
    while(c = getchar(), c >= 0 && c <= 9)
        x = x * 10 + c - 0;
    if(op) x = -x;
}
template <class T>
void write(T x)
{
    if(x < 0) putchar(-), x = -x;
    if(x >= 10) write(x / 10);
    putchar(0 + x % 10);
}
const int N = 2010;
int n,m,v,e;
int c[N],d[N];
db k[N];
db mp[N][N];
db dp[N][N][2];
int main()
{
    read(n);read(m);read(v);read(e);
    duke(i,1,n)
    {
        read(c[i]);
    }
    duke(i,1,n)
    {
        read(d[i]);
    }
    duke(i,1,n)
    {
        scanf("%lf",&k[i]);
    }
    duke(i,1,v)
    {
        duke(j,1,v)
        {
            mp[i][j] = INF;
        }
    }
    duke(i,1,e)
    {
        int x,y;
        db w;
        read(x);read(y);scanf("%lf",&w);
        if(w < mp[x][y])
        {
            mp[x][y] = w;
            mp[y][x] = w;
        }
    }
    duke(i,0,n)
    {
        duke(j,0,m)
        dp[i][j][0] = dp[i][j][1] = INF / 4;
    }
    duke(i,1,v)
    {
        mp[i][i] = mp[i][0] = mp[0][i] = 0;
    }
    duke(l,1,v)
    {
        duke(i,1,v)
        {
            duke(j,1,v)
            {
//                if(i != j && j != l && i != l)
                {
                    mp[i][j] = min(mp[i][j],mp[i][l] + mp[l][j]);
                }
            }
        }
    }
    dp[1][0][0] = 0,dp[1][1][1] = 0;
    for(int i = 2;i <= n;i++)
    {
        dp[i][0][0] = dp[i - 1][0][0] + mp[c[i - 1]][c[i]];
        for(int j = 1;j <= min(i,m);j++)
        {
            dp[i][j][0] = min(dp[i][j][0],min(dp[i - 1][j][0] + mp[c[i - 1]][c[i]],dp[i - 1][j][1] + mp[c[i - 1]][c[i]] * (1 - k[i - 1]) + mp[d[i - 1]][c[i]] * k[i - 1]));
            dp[i][j][1] = min(dp[i][j][1],min(dp[i - 1][j - 1][0] + mp[c[i - 1]][c[i]] * (1 - k[i]) + mp[c[i - 1]][d[i]] * k[i],dp[i - 1][j - 1][1] + mp[d[i - 1]][d[i]] * k[i] * k[i - 1] + mp[d[i - 1]][c[i]] * (1 - k[i]) * k[i - 1] + mp[c[i - 1]][d[i]] * (1 - k[i - 1]) * k[i] + mp[c[i - 1]][c[i]] * (1 - k[i]) * (1 - k[i - 1])));
        }
    }
    db ans = INF / 4;
    duke(i,0,m)
    {
        ans = min(ans,min(dp[n][i][0],dp[n][i][1]));
    }
    printf("%.2lf\n",ans);
    return 0;
}

P1850 換教室 概率dp