【概率DP】SRM515 NewItemShop

4 篇文章 0 订阅

题意:

要在一天内销售不超过N把魔法剑,并给出一些事件:每个事件由 Ti,Ci,Pi T i , C i , P i 这个三元组组成。
分别表示:有 Pi P i 的概率,有一个客人会在 Ti T i 时刻进店,并以 Ci C i 的价格购买魔法剑。
并且有如下约束条件:每个时刻最多只有一个人可能会进店,可能有客人一天内在多个时间存在进店的可能性,但最多只进店一次。每个客人最多只买一把剑,并且你可以选择是否卖给他。现在求在最大化收益的期望值的方案下,这个最大期望值的值。
0Ti23 0 ≤ T i ≤ 23


分析:

题目看起来很复杂,但理一理其实很简单:
有多个可能收益的事件,每个事件都有特定的收益和可能性,DP状态的定义非常简单:
DP[i][j]表示剩余i个时间,剩余j把剑的最大期望收益。
由于一个特殊的约束:一个客人不会进店多次,这也就造成了我们不能将“一个人多次进店”看作“不同的人进店”,换句话说,我们必须考虑对于某一个特定的人的影响。因此,再结合输入数据的范围,我们可以利用状态压缩,来保存每个不同的人的影响。由于人的数量不超过24,如果直接状态压缩,数据量为 224=16777216 2 24 = 16777216 ,结合前两维,很显然会超时。所以我们再来看看需要状压的本质:为了解决一个人到达多次的情况。所以我们只需要针对于这类人状压即可,也就是说,我们只针对多次访问的人状压。这样一来,最多只有12个人有多次可能会到达,所以复杂度就是 212=4096 2 12 = 4096 ,复杂度也就降下来了。

下面再说说具体转移:
在此之前,再重申一下状态定义
DP[i][j][s]表示第i个时间之后,还剩下j把剑,目前多次来的客人来的状态为s的情况下的最大期望值。

那么转移即为,若当前的客人可以来多次,那么
DP[i][j][s] D P [ i ] [ j ] [ s ]
=(1pos)DP[i+1][j][s] = ( 1 − p o s ) ∗ D P [ i + 1 ] [ j ] [ s ] //客人没来的概率,其中pos表示客人在当前时刻到来的概率,不等同于题目给出的那个值,需要处理
+posmax(DP[i+1][j1][s|s1]+val,DP[i+1][j][s|s1]) + p o s ∗ m a x ( D P [ i + 1 ] [ j − 1 ] [ s | s 1 ] + v a l , D P [ i + 1 ] [ j ] [ s | s 1 ] ) //s1表示当前客人在二进制下的状态。
初始状态很显然:
任意一个i=24的状态均为0(这一天已经结束,没有期望值)
目标状态为DP[0][n][0]
注:这个代码交在vjudge上显示0分,在topcoder的客户端上和标程拿一样的分,并且经过本地测试无误。(估计是vjudge一个神奇的bug)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<cmath>
#include<vector>
#include<iostream>
#define SF scanf
#define PF printf
#define MAXN 55
#define MAXT 30
#define MAXM 5000
#define EPS 1e-6
using namespace std;
string read;
int las;
char Read(int &x){
    x=0;
    char c;
    while(c=read[las++],las!=read.size()&&(c<'0'||c>'9'));
    x=c-'0';
    if(las==read.size())
        return 0;
    while(c=read[las++],las!=read.size()&&c>='0'&&c<='9')
        x=x*10+c-'0';
    if(las==read.size())
        return 0;
    return c;
}
struct node{
    int t;
    double val,pos;
    node () {}
    node (int t1,double val1,double pos1):t(t1),val(val1),pos(pos1) {}
};
int n,cnt;
vector<node>a[MAXN];
node time1[MAXT];
int r2[MAXM];
int used[MAXN],id[MAXN];
double dp[MAXT][MAXT][MAXM];
bool vis[MAXT][MAXT][MAXM];
double solve(int t,int sw,int s){
    if(t==24||sw==0)
        return 0;
    if(vis[t][sw][s]!=0)
        return dp[t][sw][s];
    vis[t][sw][s]=1;
    int x=time1[t].t;
    if(x==-1){
        dp[t][sw][s]=solve(t+1,sw,s);
        return dp[t][sw][s];
    }
    int s1;
    if(used[x]==1)
        s1=(1<<id[x]);
    else
        s1=0;
    if(s1&s){
        dp[t][sw][s]=solve(t+1,sw,s);
        return dp[t][sw][s];
    }
    double res1=(1-time1[t].pos)*solve(t+1,sw,s);
    res1+=time1[t].pos*max(time1[t].val+solve(t+1,sw-1,s|s1),solve(t+1,sw,s|s1));
    dp[t][sw][s]=res1;
    return res1;
}
class NewItemShop{
public:
    double getMaximum(int n1,vector<string> r1){
        n=n1;
        for(int i=0;i<r1.size();i++)
            read+=r1[i]+"|";
        int x;
        for(int i=0;;i++){
            char c=Read(x);
            r2[++cnt]=x;
            if(c==0)
                break;
            if(c=='|')
                r2[++cnt]=-1;
        }
        r2[++cnt]=-1;
        /*for(int i=1;i<=cnt;i++)
            PF("%d ",r2[i]);
        PF("|");*/
        int sum=0;
        for(int i=1;i<=cnt;i++){
            if(r2[i]==-1)
                sum++;
            else{
                a[sum].push_back(node(r2[i],double(r2[i+1]),double(r2[i+2]/100.0)));
                i+=2;
            }
        }
        /*for(int i=0;i<sum;i++){
            for(int j=0;j<a[i].size();j++)
                PF("\n{%d %lf %lf}",a[i][j].t,a[i][j].val,a[i][j].pos);
            PF("\n---------------\n");
        }*/
        cnt=0;
        for(int i=0;i<24;i++)
            time1[i].t=-1;
        for(int i=0;i<sum;i++){
            if(a[i].size()>1){
                used[i]=1;
                id[i]=cnt;
                cnt++;
            }
            double p1=1;
            for(int j=0;j<a[i].size();j++){
                double p2=a[i][j].pos/p1;
                p1-=a[i][j].pos;
                time1[a[i][j].t]=node(i,a[i][j].val,p2);
            }
        }
        //PF("%lf",solve(0,n,0));
        return solve(0,n,0);
    }
};

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值