[DP/背包]魔鬼杀手

题目描述

你生活在一个怪兽世界里。你需要用魔法反抗这些怪兽。
每个怪兽都有一定的hit points,表示他们的生命值。你可以靠施魔法,降低怪兽的hit points.每个魔法都会有一定的damage,表示会减少被攻击者damage的hit point.一个怪兽被击败了当前仅当它的hit point <= 0。另一方面,魔法是要消耗魔力的。因为你的魔力是有限的,你希望有最少的魔力击败所有的怪兽。写一个程序完成这个任务。

Input

输入按如下的格式给出:
这里写图片描述

N是怪兽的数量(1<=N<=100),Hpi表示第i个怪兽的hit point(1<=HPi<=100000),M表示可用的魔法数量(1<=M<=100),Namej是第j种魔法的名字,最长会有30个大写或小写字母,MPj是这种魔法需要消耗的魔力(0<=MPj<=99),Targetj要么是”Single”,要么是”All”,表示该魔法只攻击单个怪兽或对全体怪兽同时有效。Damagej表示对于所有攻击对象,可以减少攻击对象Damagej的hit point(0<=Damagej<=999999)
所有数字都是整数。最少有一种魔法的Damge是非零的。

Output

输出一行,包含一个整数,表示最小需要消耗的魔力。

Sample Input

3
800
15000
30000
3
Flare 45 Single 8000
Meteor 62 All 6000
Ultimate 80 All 9999

Sample Output

232

分析

首先我们明确一点,攻击顺序怎样,只要对象和魔法是一样的,结果都一样
那么容易想到先将所有怪物血削到i点,再逐个用单体攻击杀死
我们可以把群体攻击和单体攻击看成两个背包
群体攻击afi表示造成伤害为i时的最小mp值,显然方程为:
afi=min{afi,afi-dmgj
单体攻击sfi方程也一样,但是含义不同:表示至少造成i伤害所需的mp值
所以单体的还要从后往前推一遍
然后就枚举群体伤害值,再加上单体伤害值,求小即可
注意点:
1、如果有mp=0并dmg>0的技能直接输出0走人
2、如果有dmg>100000(怪物血上限),削成100000,方便DP

#include <iostream>
#include <cstdio>
#define rep(i,a,b) for (i=a;i<=b;i++)
using namespace std;
int n,m;
int hp[101],mp[101],dmg[101];
bool tar[101];
char c;
int i,j;
int f[100001],sf[100001];
int ans,p;
int main()
{
    scanf("%d",&n);
    rep(i,1,n)
    scanf("%d",&hp[i]);
    scanf("%d\n",&m);
    rep(i,1,m)
    {
        while (1)
        {
            scanf("%c",&c);
            if (c==' ') break;
        }
        scanf("%d",&mp[i]);     
        while (1)
        {
            scanf("%c",&c);
            if (c=='S')
            tar[i]=1;
            if (c=='l') break;
        }
        scanf("%c",&c);
        scanf("%d",&dmg[i]);
        dmg[i]=min(dmg[i],100000);
        if (mp[i]==0&&dmg[i]>0)
        {
            printf("0");
            return 0;
        }
    }
    rep(i,1,100000)
    f[i]=sf[i]=2147483647;
    rep(i,1,100000)
    rep(j,1,m)
    if (!tar[j]&&i-dmg[j]>=0&&dmg[j]!=0&&f[i-dmg[j]]!=2147483647)
    f[i]=min(f[i],f[i-dmg[j]]+mp[j]);
    rep(i,1,100000)
    rep(j,1,m)
    if (tar[j]&&i-dmg[j]>=0&&dmg[j]!=0&&sf[i-dmg[j]]!=2147483647)
    sf[i]=min(sf[i],sf[i-dmg[j]]+mp[j]);
    for (i=99999;i>=1;i--)
    sf[i]=min(sf[i+1],sf[i]);
    ans=2147483647;
    rep(i,1,100000)
    if (f[i]!=2147483647)
    {
        p=f[i];
        rep(j,1,n)
        if (hp[j]>i)
        p+=sf[hp[j]-i];
        ans=min(ans,p);
    }
    printf("%d",ans);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值