HDOJ 4865 Peter's Hobby(概率dp, viterbi)

原題鏈接

題目描述

題目大意

給定不同天氣的溼度概率矩陣,以及不同天氣的狀態轉移矩陣。輸入溼度序列,求此溼度序列所對應的最有可能的天氣序列。

題目分析

一道典型的概率dp題,或者叫viterbi算法。viterbi算法常用在HMM(隱馬爾科夫模型)的三個基本問題中的問題二:decoding,即Given an observation sequence and an HMM, determine the most probable hidden state sequence。
本題中,溼度是直接可觀測的,隱藏的狀態就是天氣狀態。(不過這裏不太合適,天氣狀態也是可觀測的)。
這裏引入viterbi,是爲了更形象的說明期間的涉及到的概率問題,並不是是說此題就是viterbi算法了。因爲viterbi本質就是DP,除了HMM的decoding,還可以用在其他地方。
說了這麼多,簡單描述下viterbi算法。

viterbi算法公式

式中,Vt(j)就表明在第t天天氣狀態爲j(晴天,雨天,陰天)的概率。我們求得最後一天VT(j)的概率,選取最大值,然後根據btt(j)回溯每一天的天氣狀況。
aij, 天氣狀態轉移概率;
bj(xt), emission matrix(發射矩陣),天氣爲j溼度爲xt的概率。
這兩個概率矩陣就是題目提供的兩個概率矩陣。

這裏說得比較淺陋,如果第一次接觸,可能比較難懂,直接看代碼吧。代碼中爲了把乘法轉化爲加法,利用了log。

AC代碼

#include <iostream>
#include <algorithm>
#include <vector>
#include <stack>
#include <string>
#include <cmath>
#include <cstring>
using namespace std;

double a[3][3] = {0.5, 0.375, 0.125,
                  0.25, 0.125, 0.625,
                  0.25, 0.375, 0.375};   // transition probability

double b[3][4] = {0.6, 0.2, 0.15, 0.05,
                  0.25, 0.3, 0.2, 0.25,
                  0.05, 0.10, 0.35, 0.50}; // emittion probability
double dp[55][3];   // results
int path[55][3]; // path of days
int in[55];         // input, the humidity of leaves


int solve(string leaves) {
    if (leaves == "Dry") return 0;
    if (leaves == "Dryish") return 1;
    if (leaves == "Damp") return 2;
    if (leaves == "Soggy") return 3;
    return -1;
}

string solve(int days) {
    if (days == 0) return "Sunny";
    if (days == 1) return "Cloudy";
    if (days == 2) return "Rainy";
    return nullptr;
}

void print(stack<int> sta, int count) {
    //cout << "Case #" << count << endl;
    printf("Case #%d:\n", count);
    while (!sta.empty()) {
        int val = sta.top();
        cout << solve(val) << endl;
        sta.pop();
    }
}


void init() {
    dp[0][0] = log(0.63 * b[0][in[0]]);
    dp[0][1] = log(0.17 * b[1][in[0]]);
    dp[0][2] = log(0.2 * b[2][in[0]]);
}

int main() {


    int T, N, count = 0;
    cin >> T;
    while (T--) {
        count++;
        cin >> N;
        for (int i = 0; i < N; ++i) {
            string leaves; cin >> leaves;
            in[i] = solve(leaves);
        }

        init();   // init the fisrt day.
        for (int i = 1; i < N; ++i) {
            for (int j = 0; j < 3; j++) {
                double tmp = dp[i - 1][0] + log(a[0][j] * b[j][in[i]]);
                path[i][j] = 0;
                for (int k = 1; k < 3; ++k) {
                    if (dp[i - 1][k] + log(a[k][j] * b[j][in[i]]) > tmp) {
                        tmp = dp[i - 1][k] + log(a[k][j] * b[j][in[i]]);
                        path[i][j] = k;
                    }

                }
                dp[i][j] = tmp;
            }
        }

        double maxP = dp[N-1][0];
        int maxN = 0;
        for (int i = 1; i < 3; i++) {
            if (dp[N-1][i] > maxP) {
                maxP = dp[N-1][i];
                maxN = i;
            }
        }

        stack<int> sta; sta.push(maxN);
        for (int i = N - 1; i > 0; --i) {
            sta.push(path[i][maxN]);
            maxN = path[i][maxN];
        }

        print(sta, count);

    }
    return 0;
}
發佈了40 篇原創文章 · 獲贊 4 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章