[2016-3-28 Test]

/*
小Z 的襪子
Description
ZYB 很喜歡襪子,他決定送一些襪子給機房裏的人。機房裏有n 個人,但是他們腳的尺碼可能不一樣,
一共有m 種尺碼,第i 個人的尺碼爲j 的概率爲aij(ai1+...+aim=1)。
ZYB會帶來n雙襪子,並逐個詢問機房裏的人,如果他的尺碼的襪子還有,ZYB就會送給他一雙,否則將他跳過。
ZYB想知道他帶n 雙襪子來最多期望送出幾雙。
讀入格式
第一行讀入兩個數n和m,接下來n行每行讀入m 個整數表示aij*1000。
輸出格式
輸出一個數表示答案。
Sol:
先不考慮一個人要帶多少種什麼樣的襪子
考慮不同種的襪子。
如果給定n個人,帶某種襪子,他期望送出的襪子是和別的種類的襪子無關
而又由於期望的線性性質,E(X) = p1*E(x1)+p2*E(x2)+....+pn*E(xn)
我們只要dp出n個人,第k種襪子,帶j雙期望能送出多少雙。
dp[i][j][k]表示前i個人,帶j雙尺碼爲k的襪子,期望能送出多少雙
g[i][j], n個人,帶i雙尺碼爲j的襪子,期望能送出多少雙
那麼g[i][j]=dp[n][i][j]
現在考慮不同種的襪子裝包
f[i][j], 前i種襪子,共帶j雙襪子,期望能送出多少雙
f[i][j] = max(f[i-1][k]+g[j-k][i])
就可以O(n^3)解決了
*/

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 3010
using namespace std;

int n, m;

double p[maxn][maxn/10];

double dp[110][110][110], g[110][110], f[110][110];

int main(){
    //freopen("gift.in", "r", stdin);freopen("gift.out", "w", stdout);
    scanf("%d%d", &n, &m);
    int x;
    for(int i = 1; i <= n; i ++){
        for(int j = 1; j <= m; j ++){
            scanf("%d", &x);
            p[i][j] = x / 1000.0;
        }
    }

    //前i個人,帶j雙尺碼爲k的襪子,期望能送出多少雙
    for(int k = 1; k <= m; k ++)
        for(int i = 1; i <= n; i ++)
            for(int j = 1; j <= i; j ++)
                dp[i][j][k] = (dp[i-1][j-1][k] + 1) * p[i][k] + dp[i-1][j][k] * (1 - p[i][k]);



    //g[i][j], n個人,帶i雙尺碼爲j的襪子,期望能送出多少雙
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= m; j ++)
            g[i][j] = dp[n][i][j];
    //f[i][j],前i種襪子,共帶j雙襪子,期望能送出多少雙
    //f[i][j] = max(f[i-1][k]+g[j-k][i])
    for(int i = 1; i <= m; i ++)
        for(int j = 0; j <= n; j ++)
            for(int k = 0; k <= j; k ++)
                f[i][j] = max(f[i][j], f[i-1][k] + g[j-k][i]);
    printf("%.12f\n", f[m][n]);
    return 0;
}
/*
對選擇的每一雙襪子統計貢獻。顯然選擇同一種襪子多次,貢獻會越來越小。
於是先通過計算g[n][1][i]來計算每種襪子選第一雙的貢獻。
然後每次選擇貢獻最大的進行更新。
更新時通過計算g[n][now+1][i]得出再選一雙這種襪子的貢獻,
然後加入堆中。總共只會計算n+m次貢獻。
效率:n(n+m)logm 期望得分100
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define maxn 3010
using namespace std;

int n, m;

struct Pair{
    int x; double y;
    Pair(int x = 0, double y = 0):x(x), y(y){}
    bool operator<(const Pair& k)const{
        if(y != k.y)return y < k.y;
        return x < k.x;
    }
};

priority_queue<Pair> Q;

int tot, dl1[maxn * 3], dl2[maxn * 3], fr[maxn * 3];
double p[maxn][maxn/10], a[maxn * 3][maxn], b[maxn * 3];
double ans;

inline void cal(){
    int x = dl1[tot];
    for(int i = 1; i <= n; i ++)
        a[tot][i] = a[tot][i-1]*(1-p[i][x])+a[fr[tot]][i-1]*p[i][x];
    //a[tot][i]表示前i個人不選這雙襪子的概率
    b[tot] = b[fr[tot]] + a[tot][n];
    //選現在的不選以前的+選以前的不選現在的。
}


int main(){
    freopen("gift.in", "r", stdin); freopen("gift.out", "w", stdout);
    scanf("%d%d", &n, &m);
    int x;
    for(int i = 1; i <= n; i ++){
        for(int j = 1; j <= m; j ++){
            scanf("%d", &x);
            p[i][j] = x / 1000.0;
        }
    }

    for(int i = 1; i <= m; i ++){
        dl1[i] = i, dl2[i] = 1, a[i][0] = 1;
        tot ++; cal();
        Q.push(Pair(i, 1 - b[i]));//1-b[i]表示前j只全都送出去的概率
    }

    for(int i = 1; i <= n; i ++){
        Pair P = Q.top(); Q.pop();
        ans += P.y; tot ++; fr[tot] = P.x;
        dl1[tot] = dl1[P.x], dl2[tot] = dl2[P.x] + 1;
        cal(), P.x = tot, P.y = 1 - b[tot], Q.push(P);
    }
    printf("%.12lf\n", ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章