CF 443D. Andrey and Problem 贪心 or DP

来源:互联网 发布:淘宝爱他美是真是假 编辑:程序博客网 时间:2024/05/17 21:21
题意:n个盒子,第i个盒子有奖品的概率为a[i].问选若干个盒子后,正好获得一个奖品的概率最大是多少?
n<=100,0<=a[i]<=1.0


假如选的是子集s,一共k个物品.则正好中一个奖品的概率为 tot=(p1*q2*..qk)+(q1*p2*..qk)+...(q1*q2...pk).
现在设dp[i][j] 前i个物品选j个正好中一个奖品的最大概率.
dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]*q[i]+(q1*q2..q[k-1]*p[k])) dp[i-1][j-1]相当于选j-1个时的tot.

现在只要把[q[1]..q[j-1]]求出来即可.求方案也可以递推. 总共O(n^3).


正解是贪心:从概率大的往下选 若添加一下一个能使答案增加 则加入下一个.O(nlogn) 

#include <bits/stdc++.h>using namespace std;typedef long long ll;const int N=2e2+5;int n;double p[N],q[N],dp[N][N];vector<double> v[N][N];int main(){    cin>>n;    for(int i=1;i<=n;i++)        cin>>p[i],q[i]=1-p[i];    dp[0][0]=0;    for(int i=1;i<=n;i++)    {        for(int j=1;j<=i;j++)        {            dp[i][j]=dp[i-1][j];            v[i][j]=v[i-1][j];            double res=1;            for(int k=0;k<v[i-1][j-1].size();k++)                res*=v[i-1][j-1][k];            res*=p[i];            if(dp[i][j]<dp[i-1][j-1]*q[i]+res)            {                dp[i][j]=dp[i-1][j-1]*q[i]+res;                v[i][j]=v[i-1][j-1];                v[i][j].push_back(q[i]);            }        }    }    double ans=0;    for(int i=1;i<=n;i++)        ans=max(dp[n][i],ans);    printf("%.10lf\n",ans);    return 0;}