bzoj千题计划206:bzoj1076: [SCOI2008]奖励关

http://www.lydsy.com/JudgeOnline/problem.php?id=1076

 

很容易想到方程

dp[i][j]表示抛出了i个宝物,已选宝物状态为j的期望最大得分

初始化dp[0][0]=0,其余都为负无穷

设宝物i的前提宝物集合为pre[i]

枚举第i次抛,当前已选宝物状态j,这一次抛出了第l个宝物

若 j&pre[l]==pre[l]  那么这个宝物就可以选,也可以不选

选,转移到dp[i+1][j|1<<l-1]

不选,转移到dp[i+1][j]

否则,这个宝物一定不能选,转移到dp[i+1][j]

那么问题来了,最后宝物状态集合是什么,最后输出什么?

Σ dp[n][s]/s ?

错误

因为 最后每种宝物状态出现的概率不一样

那就再递推个每种状态出现的概率?

尝试写了一发,

但状态出现的概率到后面会非常小非常小,小到让我存不了。。。

所以本思路GG

对了两个点,+递推出现概率的代码:

#include<cmath>
#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

int val[16],pre[16];

int bit[16];

double dp[101][1<<16];
double f[101][1<<16];
bool vis[101][1<<16];

const double eps=1e-7;

void read(int &x)
{
    x=0; int f=1; char c=getchar();
    while(!isdigit(c))  { if(c=='-') f=-1; c=getchar(); }
    while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
    x*=f;
}

bool dcmp(double a,double b)
{
    return fabs(a-b)<eps;
}

int main()
{
    int k,n;
    read(k); read(n);
    bit[0]=1;
    for(int i=1;i<=n;++i) bit[i]=bit[i-1]<<1;
    int x;
    for(int i=1;i<=n;++i)
    {
        read(val[i]);
        while(1)
        {
            read(x);
            if(!x) break;
            pre[i]+=bit[x-1];
        }
    }
    int s=bit[n];
    vis[0][0]=true;
    f[0][0]=1;
    for(int i=0;i<k;++i)    
        for(int j=0;j<s;++j)
            if(vis[i][j])
            {
                for(int l=1;l<=n;++l)
                    if((j&pre[l])==pre[l]) 
                    {
                        if((dp[i][j]+val[l])*f[i][j]/n>dp[i+1][j|bit[l-1]]*f[i+1][j|bit[l-1]])
                        {
                            dp[i+1][j|bit[l-1]]=dp[i][j]+val[l];    
                            f[i+1][j|bit[l-1]]=f[i][j]/n;
                            vis[i+1][j|bit[l-1]]=true;
                        }
                        else if(dcmp((dp[i][j]+val[l])*f[i][j]/n,dp[i+1][j|bit[l-1]]*f[i+1][j|bit[l-1]]))
                        {
                            f[i+1][j|bit[l-1]]+=f[i][j]/n;
                            vis[i+1][j|bit[l-1]]=true;
                        }
                    }
            }
    double ans=0;
    for(int i=0;i<s;++i) ans+=dp[k][i]*f[k][i];
    printf("%.6lf",ans); 
}
View Code

 

正解:倒推

dp[i][j] 表示抛了i个宝物,所选状态为j的最大期望得分

枚举这次抛出第l种宝物

能选,j&pre[l]==pre[l]

那么从选与不选里取最优解,dp[i][j]+=max(dp[i+1][j],dp[i+1][j|1<<l-1])

不能选 dp[i][j]+=dp[i+1][j]

对于dp[i][j] 来说,枚举n种可能抛出哪种宝物,概率是同样的

所以最后dp[i][j]/n 即是状态的期望得分

最后输出dp[n][0]

 

#include<cstdio>
#include<iostream>

using namespace std;

int val[16],pre[16];

int bit[16];

double dp[101][1<<16];

void read(int &x)
{
    x=0; int f=1; char c=getchar();
    while(!isdigit(c))  { if(c=='-') f=-1; c=getchar(); }
    while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
    x*=f;
}

int main()
{
    int k,n;
    read(k); read(n);
    bit[0]=1;
    for(int i=1;i<=n;++i) bit[i]=bit[i-1]<<1;
    int x;
    for(int i=1;i<=n;++i)
    {
        read(val[i]);
        while(1)
        {
            read(x);
            if(!x) break;
            pre[i]+=bit[x-1];
        }
    }
    int S=bit[n];
    for(int i=k;i;--i)
        for(int j=0;j<S;++j)
        {
            for(int l=1;l<=n;++l)
                if((j&pre[l])==pre[l]) dp[i][j]+=max(dp[i+1][j],dp[i+1][j|bit[l-1]]+val[l]);
                else dp[i][j]+=dp[i+1][j];
            dp[i][j]/=n;
        }
    printf("%.6lf",dp[1][0]);        
}

 

posted @ 2018-01-09 09:15  TRTTG  阅读(270)  评论(0编辑  收藏  举报