题目描述
你生活在一个怪兽世界里。你需要用魔法反抗这些怪兽。
每个怪兽都有一定的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);
}