2019国防科大校赛 E Missile

本文深入探讨了一道分组背包问题,通过预处理导弹击中目标的概率与成本,将其转化为多个具有不同价值和代价的子背包。利用动态规划求解最小成本达到特定价值,复杂度为(nlogw*(w1+w2+..wn)),提供了一个高效的解决方案。

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

https://ac.nowcoder.com/acm/contest/878/E

挺好的一道分组背包

byf跟我讲了以后我还以为是求一个期望然后得到他的代价,也就是必须要击落才能获得他的价值。(沟通还是有问题,我也总是想当然)

然而这题给了一个计算的公式,发射 Tij 枚 i 号导弹的时候有一个击中的概率Pij,而此时的价值是(Pij*W[i])四舍五入

byf的一句话提醒了我,发射一枚导弹的0.5<=p<1的概率击中,那么事实上Pij*W[i]的取值个数是log级别的个数,因为每多发一枚导弹,pij增速很快,而四舍五入就会导致取值个数很少。

那么我们可以预处理出对于(Pij*W[i])的每个取值,需要多少次发射,就可以得到他的代价

那么对于i号背包,可以看成一个背包组,里面有log个背包有不同的价值和代价

我们设f[j]为得到价值为 j 时,最小代价是多少

那么f[j]=min(f[j+1],f[i-c[i][k]]+w[i][k])

0<=j<=w1+w2+...+wn

总复杂度就是(nlogw*(w1+w2+..wn))

其实复杂度有点勉强,不过还是能过的。

#include<bits/stdc++.h>
#define maxl 510
using namespace std;

int n,m,num;
int a[maxl],w[maxl],c[maxl],f[maxl*maxl];
double r[maxl];
struct node
{
    int w,c;
};
vector <node> q[maxl];
bool flag;

inline int rnd(double x)
{
    return (int)(x+0.5);
}

inline void prework()
{
    num=0;
    for(int i=1;i<=n;i++)
        scanf("%d",&w[i]),num+=w[i];
    for(int i=1;i<=n;i++)
        scanf("%d",&c[i]);
    for(int i=1;i<=n;i++)
        scanf("%lf",&r[i]);
    for(int i=1;i<=n;i++)
        q[i].clear();
    int ww,neww,t;double sum,tmp;
    for(int i=1;i<=n;i++)
    {
        tmp=1;sum=r[i];t=1;
        ww=rnd(sum*w[i]);
        q[i].push_back(node{ww,t*c[i]});
        tmp=tmp*(1.0-r[i]);
        while(ww<w[i])
        {
            sum=sum+tmp*r[i];t++;
            neww=rnd(sum*w[i]);
            if(neww!=ww)
            {
                ww=neww;
                q[i].push_back(node{ww,t*c[i]});        
            }
            tmp=tmp*(1-r[i]);
        }
    }
}

inline void mainwork()
{
    for(int i=1;i<=num+1;i++)
        f[i]=2e9;
    f[0]=0;
    int l;
    for(int i=1;i<=n;i++)
    {
        l=q[i].size();
        for(int j=num;j>=0;j--)
        {
            f[j]=min(f[j],f[j+1]);
            for(int k=0;k<l;k++)
            if((j-q[i][k].w)>=0)
                f[j]=min(f[j],f[j-q[i][k].w]+q[i][k].c);
        }
    }
}

inline void print()
{
    int s;
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&s);
        if(s>num || s<0 )
            puts("Impossible");
        else
            printf("%d\n",f[s]);
    }
}

int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        prework();
        mainwork();
        print();
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值