魔法物品——动态规划
题目来源
题目描述
有两种类型的物品:普通物品和魔法物品。普通物品没有魔法属性,而魔法物品拥有一些魔法属性。每种普通物品有一个价值 p ,但每种魔法物品有两种价值:鉴定前的价值 p1 ,和鉴定后的价值 p2 (当然,p2 总是大于 p1);
为了鉴定一个魔法物品,你需要购买一个鉴定卷轴,用它来鉴定魔法物品。鉴定一个魔法物品以后,鉴定卷轴便会消失。每个鉴定将会消耗 pi 元钱,如果没有足够的钱你将无法购买任何的鉴定卷轴。
现在,你正在一个集市中,同时拥有很多件物品,你知道每件物品的价值并且想要出售全部物品。那么,你最多能够获得多少钱呢?
你可以假定:
1 开始的时候你没有钱
2 所有的魔法物品都还没有被鉴定
3 只要你有足够的钱,你可以购买任意多的卷轴。
输入
第一行有两个整数 N 和 Pi (0 < Pi <= 5000)表示你拥有物品书和一个鉴定卷轴的价格
接下来的 N 行,每行给出一件物品的价格
对于每件普通物品。那一行仅有一个整数 P (0 < P <= 10000).
对于每件魔法物品,那一行将会有两个整数 P1 和 P2 (0 < P1 < P2 <= 10000)。
输出
一个整数表示你最多能够获得多少钱
样例输入
2 10
10
20 100
样例输出
100
数据规模
对于 30 % 的数据 N <= 50;
对于 100% 的数据 N <= 1000;
解题思路
- 如果一个魔法物品,它的魔法值减去它的价值小于魔法卷轴的价格,那就说明购买魔法卷轴来件鉴定这件物品是无用的,因此将这类魔法物品视为普通物品
- 普通物品由于没有魔法价值,因此只能将其出售,可以将普通物品的价值和视为初始资金 v (下同)
- 分类讨论 v 与 p (魔法卷轴价格,下同) 的关系:
- 如果 v >= p:
- 说明初始资金足够购买一个魔法卷轴,将购买卷轴后鉴定出的魔法物品按照魔法物品的价格售出后,将会获得更多的钱,而此时手中所拥有的钱肯定能够继续购买魔法卷轴来鉴定魔法物品
- 因此如果初始资金 v >= p,那么可以将所有的魔法物品鉴定出来,并且按照魔法价格出售
- 如果 v < p:
- 需要将一部分的魔法物品按照其价格售出,是资金足够购买魔法卷轴
- 当能够购买一个魔法卷轴后按照 v >= p 的思路购买所有的魔法物品
- 此部分需要使用动归计算最小损失和
- 如果 v >= p:
- 注意 : 数据中会出现售出所有的魔法物品仍然不能购买一个魔法卷轴的情况
- 注意 : 所有数据均不会出现初始资金 v 能够购买魔法卷轴的情况
源代码
#include<iostream>
#include<cstdio>
#include<sstream>
#include<cstring>
#include<algorithm>
using namespace std;
int f[10000005];
int n,p;
int th_len; // 魔法物品的个数
struct thing {
int v;
int p;
int t;
}th[1005];
int main(){
freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
string in;
cin >> n >> p;
getline(cin,in); // 读取换行
int v = 0,allv = 0,callv;
for(int i = 1;i <= n;i++){
stringstream st;
getline(cin,in);
st << in;
int a,b;
st >> a;
allv += a;
if(st >> b && b - a > p){ // st >> b 读取成功 代表存在魔法属性
th[++th_len].v = a;
th[th_len].p = b;
th[th_len].t = b - a - p; // 损失差,方便下一步的动归求解
}else
v += a;
}
callv = allv; //由于接下来会对 allv 进行操作,所以用 callv 保存所有物品的价格和,以便出现卖出所有魔法物品后仍无法购买卷轴时直接输出
for(int i = 0;i <= th_len;i++){
for(int j = 1;j <= allv;j++){
f[j] = 999999999;
}
}
if(p > v){ //当初始资金无法购买一个魔法卷轴
for(int i = 1;i <= th_len;i++){
for(int j = allv;j >= 1;j--){
f[j] = f[j];
if(j - th[i].v>= 0){
f[j] = min(f[j],f[j - th[i].v] + th[i].t);
}
}
}
int mi = 999999999;
for(int i = p - v;i <= allv;i++)
if(f[i] < mi)
mi = f[i];
for(int i = 1;i <= th_len;i++)
allv += th[i].t;
if(allv - mi <= 0)
cout << callv;
else
cout << allv - mi;
}else{ // 初始资金能够购买一个魔法卷轴
for(int i = 1;i <= th_len;i++)
allv += th[i].t;
cout << allv;
}
return 0;
}