题解:AT_abc402_e [ABC402E] Payment Required

题目传送门

题目描述如下:竞赛共有 \(N\) 道题目。第 \(i\) 道题目,得分为 \(S_i\) 分。每次提交需花费 \(Cᵢ\) 日元。高桥君提交第 \(i\) 道题目时,有 \(Pᵢ\%\) 的概率能解决该题(每次提交结果独立)。即使多次提交同一题,得分不会重复计算。
高桥君有 \(X\) 日元,总花费不能超过 \(X\) 日元。他可以根据之前的提交结果 动态调整策略。

容易想到这是一道期望 \(dp\),但是因为 \(n\leq 8\),所以我们还可以用状压 \(dp\) 来做。考虑转移方程,定义 \(dp_{i,j}\),为现在的情况是 \(i\),还剩 \(j\) 元时的最大得分。因为 \(i\) 是一个二进制数,第 \(k\) 为上为 \(0\) 时,表示还没做这题,为 \(1\) 时表示做了这题。那么转移方程就比较清晰了,见代码:

f[i][j]=max(f[i][j],f[i-c[k]][j]*(100-p[k])*1.00/100+(f[i-c[k]][j|(1<<(k-1))]+s[k])*p[k]*1.00/100);

我解释一下方程式,\(dp_{i-C_k,j}\times (100-P_k)\div100\) 表示做这道题没做对的贡献,另一部分表示做这道题做对的贡献。

代码。

#include<bits/stdc++.h>
#define ll long long
#define db double
using namespace std;
const int N=1010101,M=3010;
ll n,m,c[N],s[N],p[N];
db f[5010][310];
int main(){
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++)scanf("%lld%lld%lld",&s[i],&c[i],&p[i]);
	for(int i=1;i<=m;i++){
		for(int j=0;j<(1<<n);j++){
			for(int k=1;k<=n;k++){
				if(c[k]<=i&&((j>>(k-1))&1)==0){
					f[i][j]=max(f[i][j],f[i-c[k]][j]*(100-p[k])*1.00/100+(f[i-c[k]][j|(1<<(k-1))]+s[k])*p[k]*1.00/100);
				}
			}
		}
	}
	printf("%0.10lf",f[m][0]);
}
posted @ 2025-09-10 21:39  一班的hoko  阅读(3)  评论(0)    收藏  举报