题目链接:http://cstest.scu.edu.cn/soj/problem.action?id=2222
题目大意:就是有一些物品,有两种属性,可以加hp,也可以加分数,但是只有你在血量满的时候,吃到这个物品才会加分数,不然没满的话,只能加血量。。
要求怎么样才能使分数最多。
思路:
既然要使得到的分数最多,那么就需要让用来加血的物品的分数总和最小这样就能保证剩下的物品都用来加分数,因为所有物品的分数总和是一定的。让白费掉的分数最小,剩下的分数就是最大的。
但是有个地方要注意的就是,背包的容量不再局限于总的血槽,因为我可以使血量加满,漫过血槽上限。在循环的起点和终点的地方需要改进一下。
给出状态转移方程,dp[j] = min(dp[j],dp[j-c[i]]+w[i])。
还有要注意的就是只有当前背包容量大于血槽上限才看是否需要更新最少的分数。。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define M 20009
#define INF 0x3f3f3f3f
int dp[M],c[M],w[M];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,v;
scanf("%d%d",&v,&n);
for(int i = 0;i <= v+10000;i++) //v+10000 因为最多超过一个物品
dp[i] = INF;
dp[0] = 0;
int sum = 0;
int sum1 = 0;
for(int i = 0;i < n;i++)
{
scanf("%d %d",&c[i],&w[i]);
sum += w[i];
sum1 += c[i];
}
if(sum1<v) //如果总共的血量也不能加满 那么直接就不可能吃到分数。
{
printf("0\n");
continue;
}
int minx = INF; //加满血所需要最少的分数
for(int i = 0;i < n;i++)
for(int j = v+c[i];j >= c[i];j--)
{
dp[j] = min(dp[j],dp[j-c[i]]+w[i]);
if(j>=v && dp[j]<minx) //只有当前背包容量大于血槽上限才看是否需要更新minx
minx = dp[j];
}
printf("%d\n",sum-minx);
}
return 0;
}