zoukankan      html  css  js  c++  java
  • [SCOI2008] 奖励关

    前言

    高筑墙,广积粮,缓称王。

    题目

    洛谷

    讲解

    此部分与正解无关。

    首先它与前面吃了哪些有关系,所以我想的是了令 \(dp_{i,S}\) 表示前 \(i\) 天吃了状态为 \(S\) 的宝物的最大期望值。

    问题来了,怎么转移,你从前面转移过来的状态可能不存在,而我们要求的又是期望,状态数量都不知道怎么求期望呢?

    然后我又想到刚做的这道题

    既然吃了哪些做不了,那么我把状态改为前 \(i\) 天选择吃和不选择吃状态为 \(S\) 的宝物。

    但是这样更不可做了,因为,你连你吃了哪些都不知道,只知道要吃哪些,无法转移。

    我们再尝试修锅,令 \(f_{i,S}\) 表示前 \(i\) 天打算吃状态为 \(S\) 的宝物,\(g_{i,S}\) 表示前 \(i\) 天吃了状态为 \(S\) 的宝物的最大期望值,然而 \(g\) 和刚开始的错误思路一样,无法转移,然后我又想到自己枚举,但是算了一下过不去,至此我嗝屁了。


    正解竟然是逆推。

    不过想想也挺合理的,因为倒着的状态一定是合法的,令 \(dp_{i,S}\) 表示前 \(i-1\) 天吃了的宝物状态为 \(S\)\(i\)\(k\) 天的最大期望得分。

    那么答案就是 \(dp_{1,\varnothing}\),时间复杂度 \(O(kn2^n).\)

    代码

    精简
    //12252024832524
    #include <bits/stdc++.h>
    #define TT template<typename T>
    using namespace std;
    
    typedef long long LL;
    const int MAXN = 15;
    const int MAXK = 105;
    const int INF = 0x3f3f3f3f;
    int k,n;
    int p[MAXN],pre[MAXN];
    
    LL Read()
    {
    	LL x = 0,f = 1; char c = getchar();
    	while(c > '9' || c < '0'){if(c == '-') f = -1;c = getchar();}
    	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
    	return x * f;
    }
    TT void Put1(T x)
    {
    	if(x > 9) Put1(x/10);
    	putchar(x%10^48);
    }
    TT void Put(T x,char c = -1)
    {
    	if(x < 0) putchar('-'),x = -x;
    	Put1(x); if(c >= 0) putchar(c);
    }
    TT T Max(T x,T y){return x > y ? x : y;}
    TT T Min(T x,T y){return x < y ? x : y;}
    TT T Abs(T x){return x < 0 ? -x : x;}
    
    double dp[MAXK][1<<15];
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	k = Read(); n = Read();
    	for(int i = 0,val;i < n;++ i) 
    	{
    		p[i] = Read();
    		while(val = Read()) pre[i] |= 1<<(val-1);
    	}
    	for(int i = k;i >= 1;-- i)
    		for(int S = 0;S < (1<<n);++ S)
    		{
    			for(int j = 0;j < n;++ j)
    				if((pre[j] & S) == pre[j]) dp[i][S] += Max(dp[i+1][S],dp[i+1][S|(1<<j)] + p[j]);
    				else dp[i][S] += dp[i+1][S];
    			dp[i][S] /= n;
    		}
    	printf("%.6f\n",dp[1][0]);
    	return 0;
    }
    

    总结

    当转移需要前面的某些状态,可以将转移顺序逆序而状态顺序,此时状态必定合法,更方便转移。

  • 相关阅读:
    ps_基于2020的官方教程
    杂记_好玩的
    linux _文件目录与权限
    levelDb笔记
    《好学的C++ 第2版》 第9章 一些高级编程技术
    《好学的C++ 第2版》 第8章 文件-电子存储
    《好学的C++ 第2版》 第7章 字符串--分析文本
    《好学的C++ 第2版》 第6章 指针--我知道数据在哪里
    《好学的C++ 第2版》 第5章 数组--都给我排好队
    《好学的C++ 第2版》 第4章 函数--分工与合作
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/15571022.html
Copyright © 2011-2022 走看看