问题描述:
度度熊为了拯救可爱的公主,于是与邪恶大魔王战斗起来。
邪恶大魔王的麾下有n个怪兽,每个怪兽有a[i]的生命值,以及b[i]的防御力。
度度熊一共拥有m种攻击方式,第i种攻击方式,需要消耗k[i]的晶石,造成p[i]点伤害。
当然,如果度度熊使用第i个技能打在第j个怪兽上面的话,会使得第j个怪兽的生命值减少p[i]-b[j],当然如果伤害小于防御,那么攻击就不会奏效。
如果怪兽的生命值降为0或以下,那么怪兽就会被消灭。
当然每个技能都可以使用无限次。
请问度度熊最少携带多少晶石,就可以消灭所有的怪兽。
———-(吐槽百度的命题)
input:
本题包含若干组测试数据。
第一行两个整数n,m,表示有n个怪兽,m种技能。
接下来n行,每行两个整数,a[i],b[i],分别表示怪兽的生命值和防御力。
再接下来m行,每行两个整数k[i]和p[i],分别表示技能的消耗晶石数目和技能的伤害值。
数据范围:
1<=n<=100000
1<=m<=1000
1<=a[i]<=1000
0<=b[i]<=10
0<=k[i]<=100000
0<=p[i]<=1000
这道题是一道完全背包问题
dp[defend][j] 就表示防御为defend消耗血量为j需要的最小晶石(stone)
一边看代码一边理解吧,代码里有一定的注释
//首先,声明我进的一个坑,刚开始做的时候以为int就足够使用了
//所以stone(晶石)和dp数组就都是用的int,结果提交的时候一直wa,很无奈最后抱着试试的心态,改成了longlong,过~
#include<string.h>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
//下面用了结构体,其实用数组完全可以,并且效率还会提升
struct bear //度度熊
{
int k;
int p;
};
struct monster
{
int a, b;
};
int main(void)
{
freopen("in.txt", "r", stdin); //重定向,提交时一定要删掉啊
int n, m;
long long dp[11][1010];
while(scanf("%d%d", &n, &m)==2)
{
memset(dp, 0x3f, sizeof(dp));
int att_max=0, def_max=0;
int each_min[11]; //这里我命名有点问题,这个each_min是表示,每一个防御值里血量的最大值,可以减少循环的次数
memset(each_min, 0, sizeof(each_min));
vector<bear> be_ve;
vector<monster> mon[11];
monster temp;
for(int i=0;i<n;++i)
{
scanf("%d%d", &temp.a , &temp.b);
if(def_max<temp.b)
def_max=temp.b; //求得防御的最大值
if(each_min[temp.b]<temp.a)
each_min[temp.b]=temp.a;
mon[temp.b].push_back(temp);
}
bear be;
for(int i=0;i<m;++i)
{
scanf("%d%d",&be.k, &be.p);
if(att_max<be.p)
att_max=be.p; //攻击的最大值
be_ve.push_back(be);
}
if(att_max<=def_max) //攻击的最大值小于等于防御 就无法攻破
printf("-1\n");
else
{
for(int defend=0;defend<11;defend++)
{
dp[defend][0]=0;
for(int j=1;j<=each_min[defend];++j)
{
for(int s=0;s<m;++s)
{
if(be_ve[s].p>defend)
{
if((j>=be_ve[s].p-defend))
{
dp[defend][j]=min(dp[defend][j], dp[defend][j-be_ve[s].p+defend]+be_ve[s].k); //状态转移方程
}
else
{
if(dp[defend][j]>be_ve[s].k)
dp[defend][j]=be_ve[s].k; //这里需要注意一下,当攻击大于防御的时候,可能攻击一下,就干掉了小怪兽,这里遍历找到一下就能打倒小怪兽的最小晶石
}
}
}
}
}
long long stone=0; //我差点在这里翻车,以后要长记性了,充分考虑数 的范围
for(int i=0;i<11;++i)
{
for(int j=0;j<mon[i].size();++j)
{
stone+=dp[i][mon[i][j].a]; //防御为i血量为a的monster消耗的最小晶石,把每一个monster都遍历一下,在dp中找到并加在一起,就OK了
}
}
printf("%I64d\n", stone);
}
}
return 0;
}
只要背包问题理解的深刻,这个问题也并不难解决,欢迎留言~