题解:CF167B Wizards and Huge Prize
CF167B 题解(DP, 1800)
(本题解主要是对目前题解区已有做法做了统一的梳理)
题意
给定初始背包容量
题解
本题非常适合在学习完背包 DP 和简单概率 DP 后进行综合练习,因为其不仅综合了这两个知识点,还搭配上了两个非常较复杂的边界处理。
-
dp_{i+1,j+1,k+a_{i+1}}+=dp_{i,j,k}\times p_{i+1} - 说明:
a_i 对于物品来说体积为-1 、对于背包来说体积为正数,都符合转移式,因此转移式中不需要区分物品和背包。
这个转移有两个问题:
- 直接做的状态数是
n\times n\times n^2=n^4 的,会超时; - 由于只要求最终能装下所有赢下的物品,过程中其实是允许背包容量为负的,但目前状态未考虑这些概率。
对于问题一:由于容量最多到
对于问题二,有两个解决的方法:
- 转移时,将所有背包放在所有奖品之前,这样再转移的时候,所有背包剩余容量的状态就确实不用考虑了(因为这些状态都对应了装不下奖品的情况)。直接使用上面的转移即可。
- 将
k<0 的状态真的引入,发现负数状态依然可以将k<-n 的状态都算到k=-n 里,从而k 需要考虑的范围就是[-n,n] 。代码实现时,可以给k 这个维度整体偏移n ,将区间移到[0,2n] 。
时间复杂度
代码
这里给出对于问题二,使用第一种解决办法的代码:
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=(a);i<=(n);i++)
#define per(i,a,n) for(int i=(n);i>=(a);i--)
#define pb push_back
#define SZ(v) ((int)v.size())
#define fs first
#define sc second
#define all(x) (x.begin()),(x.end())
typedef long long ll;
typedef double db;
typedef pair<int,int> pii;
struct item{
int a;
double p;
}c[210];
bool cmp(item& x,item& y){
return x.a>y.a;
}
int n,m,k;
double dp[210][210][210];
int main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>m>>k;
rep(i,1,n){
cin>>c[i].p;
c[i].p/=100;
}
rep(i,1,n) cin>>c[i].a;
sort(c+1,c+1+n,cmp);
dp[0][0][min(n,k)]=1;
rep(i,0,n-1){
rep(j,0,n){
rep(k,0,n){
if(k+c[i+1].a>=0) dp[i+1][j+1][min(k+c[i+1].a,n)]+=c[i+1].p*dp[i][j][k];
dp[i+1][j][k]+=(1-c[i+1].p)*dp[i][j][k];
}
}
}
double ans=0;
rep(j,m,n){
rep(k,0,n){
ans+=dp[n][j][k];
}
}
cout<<fixed<<setprecision(8)<<ans<<endl;
return 0;
}