ABC 402 E - Payment Required
ABC 402 E - Payment Required
有向图随机游走
我还是得好好学学期望。
题意
有 \(n\) 个问题,每个问题分数是 \(s_i\),提交一次的花费是 \(c_i\),提交一次通过的概率是 \(p_i\)。
每个问题可以多次提交,多次通过算一次得分。
你可以根据之前的提交情况决定之后的操作。
你有 \(x\) 块钱,问最大期望得分是多少。
\(n \le 8, s \le 2718, c,s \le 5000\)。
思路
考虑期望 DP。
先刻画状态,本质不同的状态只需要考虑,目前还剩下多少钱,已经通过了哪些题目(或者还有哪些题目没有通过)。因为 \(n \le 8\),所以状态数是 \(O(2^n x)\) 是可以接受的。
\(f_{res,i}\) 表示还剩 \(res\) 元,已经通过了集合 \(i\) 的题目。
将每个状态的有向图随机游走建立出来,然后就可以推状态转移方程。每个状态由它可以到达的状态转移过来。
在状态 \((res,i)\) 时,下一步可以提交任意还没有通过的题目,假设提交题目 \(j\)。
- 有 \(p_j\) 的概率成功,得到 \(s_j\) 分,后继状态是 \((res-c_j,i \cup j)\)。
 - 有 \(1-p_i\) 的概率失败,不得分,后继状态是 \((res-c_j,i)\)。
 
所以
\[f_{res,i} = \max\{f_{res,i}, p_i \cdot (f_{res-c_j,i \cup j} + s_j) + (1-p_i) \cdot f_{res-c_j,i}\}
\]
转移复杂度 \(O(n)\)。
答案就是初始时有 \(x\) 元,已经解决了 \(\emptyset\) 题。
时间复杂度 \(O(2^n n x)\)。
code
#include<bits/stdc++.h>
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
using namespace std;
typedef long long ll;
namespace wing_heart {
	constexpr int N=8,X=5e3+7;
	int n,x;
	int s[N+1],c[N+1],p[N+1];
	double f[X][1<<N];
    void main() {
		sf("%d%d",&n,&x);
		rep(i,1,n) {
			sf("%d%d%d",&s[i],&c[i],&p[i]);
		}
		rep(res,1,x) { // 目前还剩下的钱
			rep(i,0,(1<<n)-1) { // 目前已解决的问题
				rep(j,1,n) {
					int to=i|(1<<(j-1));
					if(i==to || res<c[j]) continue;
					double P=1.0*p[j]/100;
					f[res][i] = max(f[res][i], P*(f[res-c[j]][to]+s[j]) + (1-P)*f[res-c[j]][i]);
				}
			}
		}
		pf("%.9lf",f[x][0]);
    }
}
int main() {
    #ifdef LOCAL
    freopen("in.txt","r",stdin);
    freopen("my.out","w",stdout);
    #endif
    wing_heart :: main();
}
本文来自博客园,作者:wing_heart,转载请注明原文链接:https://www.cnblogs.com/wingheart/p/18838795

                
            
        
浙公网安备 33010602011771号