NUDT校赛 E Missile 背包dp 概率

该博客介绍了一种使用动态规划(DP)解决概率优化问题的策略,涉及导弹命中目标的概率和成本。在总价值不少于s的情况下,如何以最小成本确保至少一枚导弹命中目标。博主详细阐述了思路,包括概率计算、DP状态定义、递推公式及初始化,并给出了关键代码片段。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

题意: 有n个目标, n种导弹, 第i种导弹只能打第i个目标, 给出每种导弹命中的概率, 每种导弹的成本(元/个), 总价值为

总成本为

问在总价值不小于s的情况下, 最小成本是多少.

思路:

①概率问题: 假设某火箭发射k次, 全都没命中的概率是 (1-p)^{k}, 那么命中的概率就是1-(1-p)^{k}, 那么一种火箭的价值贡献就是round(e*(1-(1-p)^{k})), 一种火箭的成本就是k*c.

②考虑dp dp[i]表示当前总花费, i表示当前的最大总价值.

③递推式: dp[i]=dp[i-round(w[j]*(1-(1-p[j])^{k}))]+k*v[j]

④初始化: 将每一个价值都置为inf, 表示要付出无穷大的花费才能达到当前价值.dp[0]=0, 0价值当然不需要花费就能达到.

⑤顺序:

每种导弹->每个价值从大到小->此种导弹的个数.

原因: 将当前种类的导弹的影响都考虑完,再考虑下一种导弹.

每个价值从大到小 保证 只用之前导弹得到的代价 更新 当前状态的代价. 如果从小到大更新, 因为dp数组只有1维, 不可避免的, 就会让已经更新过x类导弹的低状态影响到高状态.我们要保证每个状态都是由没有此类导弹的状态得到, 而背包都是由低状态得到高状态, 所以要先枚举高状态(此时低状态都是未更新的), 再更新低状态.

另外, 最后两维也不能反, 每种炮弹的个数只能有一次贡献, 而如果先枚举个数再枚举价值的话, 某状态可能受到两次不同个数导弹的影响而改变.

⑥如果 低价值的成本>高价值的成本, 那么 高价值的成本就可以赋给低价值的成本(因为如果小的成本就能得到大的价值, 那么他当然能的到小的价值).

代码:

#include<bits/stdc++.h>
#define fuck(x) std::cout<<"["<<#x<<"->"<<x<<"]"<<endl;
using namespace std;
typedef long long ll;

const int M=2e5+5;
const int inf=1e9+5;
const int mod=1e9+7;
//memset(a,0x3f,sizeof(a));
int dp[50005];
//   dp[i]=dp[ i - w[j]*(1- ( 1- p[j]) ^k) ] + v[j] *k
int n,m,s;
int w[505];
int v[505];
double p[505];
int sum=0;

int main() {
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++) {
        scanf("%d",&w[i]);
        sum+=w[i];
    }
    for(int i=1; i<=n; i++) {
        scanf("%d",&v[i]);
    }
    for(int i=1; i<=n; i++) {
        scanf("%lf",&p[i]);
    }
    fill(dp,dp+50005,inf);
    dp[0]=0;
    for(int j=1; j<=n; j++) {
        for(int i=sum; i>=0; i--) {
            double tmp=1;
            for(int k=0; k<=10; k++) {
                int now=round(w[j]*(1- tmp ));
                if(i>=now)
                    dp[i]=min(dp[i],dp[ i - now   ] + v[j] * k );
                tmp=tmp*( 1- p[j]);
            }
        }
    }
    for(int i=sum; i>=0; i--) {
        dp[i]=min(dp[i],dp[i+1]);
    }
//    for(int i=0;i<505;i++){
//        printf("[%d]:%d ",i,dp[i]);
//    }
//    puts("");
    for(int i=0; i<m; i++) {
        scanf("%d",&s);
        if(dp[s]==inf) {
            printf("Impossible\n");
        } else
            printf("%d\n",dp[s]);
    }
    return 0;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值