一个简单二维背包问题。
主要思想:通过二维矩阵mapa[j][i]求解,横坐标代表耐力值,纵坐标代表经验值。con代表当前所能获得的最大经验值
1.初始化mapa[0][0]到mapa[m][N]为0。con为0。
2.初始情况下仅有mapa[m][0]= s ;即已获得零点经验,还剩m点耐力,还能杀s只怪。
3.读入一个怪的信息(经验值a ,耐力b ),而后遍历第0到第con列的每一个单元,当满足:
①mapa[j][i]>0 (代表还可以杀怪)。
②j-b>=0 (代表杀此怪的耐力足够)
③mapa[j-b][i+a]<=mapa[j][i]-1 (代表杀了此怪后剩余次数比之前到达单元mapa[j-b][i+a]的剩余次数还要多)
将mapa[j-b][i+a]的剩余次数更新为mapa[j][i]-1。
否则不予更新。
4.重复步骤3 k次以读取完所有怪的信息
5.判断con是否大于
以下为代码实现:
#include <iostream> #include <cstdio> #include <cstdlib> #include <memory.h> #include <cstring> #include <queue> #include <cmath> #include <algorithm> #define IN freopen("in.txt", "r", stdin) #define OUT freopen("out.txt", "wb", stdout) #define max(a, b) (((a) > (b)) ? (a) : (b)) #define min(a, b) (((a) < (b)) ? (a) : (b)) #define N 210 using namespace std; /** 注意:这里由于对所获经验上限不确定,故(N-n)需要大于单次杀怪所获的最大经验值(防止数组越界), 这里是(N-100)>100,故 N 应大于200 */ int main() { int mapa[N][N]; //mapa[i][j]代表在还剩 i 点忍耐值、获得了 j 点经验的情况下还能杀mapa[i][j]个怪 int n,m,k,s; //n:所需经验值、m:忍耐度、k:怪的种类、s:最多杀怪数 int a,b,i,j; //a:经验值、b:会减少的忍耐度 int con,answer; //con:用于记录当前情况下所能达到的最大经验值(用于节省时间,防止在不可能到达的经验值下浪费时间遍历) //answer:用于记录当前情况下,达到n点经验值所能保留的最大耐力 while(scanf("%d%d%d%d",&n,&m,&k,&s)!=EOF) { con=answer=0; //初始化 memset(mapa[0],0,N*(m+1)*sizeof(int)); mapa[m][0]=s; while(k-->0) { cin>>a>>b; //读入第k个怪的经验值及耐力值 for(i=0;i<=con && i<=n;i++) //i<=n:当i>n 时再搜索下去就没意义了(经验值已经满了), i<=con同理。 //完全背包思想,从0到min(con,n)遍历 { for(j=m;j>=0;j--) //对每一个 if(mapa[j][i]&&j-b>=0&&mapa[j-b][i+a]<=mapa[j][i]-1) //还有次数,忍耐度有剩余,且次数比该位置之前的剩余次数还多 { if(i+a>con) //更新所能获得的最大经验值 con=i+a; if(i+a>=n&&answer<j-b) //经验值够了且大于之前最大值,进行替换 answer=j-b; mapa[j-b][i+a]=mapa[j][i]-1; } } } if(con>=n) //可以获得 n 点经验,输出结果 cout<<answer<<endl; else cout<<"-1"<<endl; } return 0; }