zoukankan      html  css  js  c++  java
  • BZOJ1076/Luogu2473 奖励关(SCOI2008)状压DP+期望DP

    题意:给n(n<=15)种宝物宝物有价值w且每个宝物有一个前置宝物(即你必须先吃过它的所有前置宝物至少一次才能吃该宝物),共有m轮游戏,每一轮会在n种宝物等概率选一个出来,因为宝物价值可正可负你可以选择吃掉或者不吃,问m轮后你能获得的最大价值。

    解法:这道题挺有意思的。看到n<=15容易想到用状压DP,于是我的第一想法是因为

    但是此题起点是一定的但是终点不一定,所以从终点往回推可能会简单一些,于是设dp[x][S]代表1~x-1轮的状态为S,x~m轮的最大期望为dp[x][S] 。一定要重点注意这个状态的设计,这样设计状态会使得状态转移方程也比较好写:首先是对于每一个dp[i][j]要枚举k代表在此状态下等概率发的牌是第k种宝物

    dp[i][j]+=max(dp[i+1][j|(1<<k-1)]+w[k],dp[i+1][j]);  (k在状态j下能吃,选择吃或不吃)

    dp[i][j]+=dp[i+1][j]; (k在状态下不能吃,没得选择,肯定不能吃)

    加完之后是期望和,那么dp[i][j]/=n;  代表期望。

    初始化dp[m+1][]=0 ,答案就是dp[1][0]。

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=17;
    int n,m,w[N];
    vector<int> G[N];
    double dp[110][1<<N];  //dp[x][S]代表1~x-1轮的状态为S,x~m轮的最大期望为dp[x][S] 
    
    bool check(int x,int S) {
        for (int i=0;i<G[x].size();i++) {
            int y=G[x][i];
            if ((S&(1<<(y-1)))==0) return 0;
        }
        return 1;
    }
    
    int main()
    {
        cin>>m>>n;
        for (int i=1;i<=n;i++) {
            scanf("%d",&w[i]); int t;
            while (scanf("%d",&t) && t) G[i].push_back(t);
        }
        
        for (int i=m;i;i--)
            for (int j=0;j<(1<<n);j++) {
                for (int k=1;k<=n;k++)
                    if (check(k,j)) dp[i][j]+=max(dp[i+1][j|(1<<k-1)]+w[k],dp[i+1][j]);
                    else dp[i][j]+=dp[i+1][j];
                dp[i][j]/=(double)n;        
            }
        printf("%.6lf
    ",dp[1][0]);    
        return 0;
    }
  • 相关阅读:
    《Python 学习手册4th》 第十一章 赋值、表达式和打印
    《Python 学习手册4th》 第十章 Python语句简介
    视图
    表约束
    表值参数
    表操作
    MSSQL数据批量插入优化详细
    SQL Server表分区
    表分区中的分区交换
    maven命令和maven插件
  • 原文地址:https://www.cnblogs.com/clno1/p/11629615.html
Copyright © 2011-2022 走看看