[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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值