【校内互测】魔法物品

245 篇文章 0 订阅

有两种类型的物品:普通物品和魔法物品。普通物品没有魔法属性,而魔法物品拥有一些魔法属性。每种普通物品有一个价值P,但每种魔法物品有两种价值:鉴定前的价值P。和鉴定后的价值P2(当然,P2总是大于P。)。

为了鉴定一个魔法物品,你需要购买一个鉴定卷轴,用它来鉴定魔法物品。鉴定完一件魔法物品以后,鉴定卷轴便会消失。每个鉴定将会消耗Pi元钱,如果没有足够的钱,你将无法购买任何鉴定卷轴。

现在,你正在一个集市中,同时拥有很多物品。你知道每件物品的价值并且想要出售全部物品。那么,你最多能够获得多少钱呢?

    你可以假定:

    ★开始的时候你没有钱。

    ★所有的魔法物品都还没有被鉴定。

    ★只要你有足够的钱,你可以购买任意多的鉴定卷轴。

输入magic.in

    第一行有两个整数N和Pi(0<Pi≤5000),表示你拥有的物品数和一个鉴定卷轴价格。

    接下来N行,每行给出一件物品的价格。

    对于每件普通物品,那一行仅有一个整数P(0<P≤10000)。

    对于每件魔法物品,那一行将会有两个整数P1和P2(0<P1<P2≤10000)。

输出magic.out

一个整数表示你最多能够获得多少钱。

样倒输入

  2 10

  10

  20 100

样例输出

  100

数据规模

  对于30%的数据N≤50;

  对于100%的数据N≤1000。

【题解】

由题意可知所有普通物品或者是P2-P1<=P的物品都可以直接卖掉。

卖掉了之后我们手里有了一些钱,这个时候进行判断,如果这些钱足够买一个卷轴的话,我们一定可以在每卖下一个物品之前利用手中的钱买一个卷轴,然后把它鉴定,且这样做一定是获益最大的。

你会发现上面这个结论一定是正确的。为什么呢?首先,剩下的这些满足的特征一定是P2-P1>P,也就是说,先买一个卷轴将它鉴定,然后再卖出去一定是赚钱的;其次,你会发现由于P2-P1>P,所以P2一定大于P,也就是说,你如果按照鉴定完了的价钱卖出的话,你一定有足够的钱再买一个卷轴,然后依次类推,你一定可以买卷轴卖物品买卷轴卖物品一直到卖掉所有的物品。这样的情况是最简单的,就是模拟一下买卷轴和买物品就行了。

但是如果这些钱不够买一个卷轴,那么我们就要按照鉴定前的价格卖出一些物品,使我们手里的钱够买一个卷轴,之后问题就转化成了上面的第一种情况。但是用哪些物品来凑钱呢?我们定义一个物品的亏损=(P2-P)-P1(由P2-P1>P可知这个式子一定大于0,其含义为使它鉴定的收益和不使它鉴定直接卖出的收益之差),我们要选出一些物品,条件是使这些物品按照原价卖出后加上原来手里有的钱足够买一个卷轴,且总亏损最小。

很容易想到用背包来解。f[v]表示将物品恰好放入体积为v的背包里的最小亏损,然后枚举一下满足条件的体积即可(这样做的话时间复杂度有点不科学,如果数据很强的话是可以卡死的,但是这是我会的最好的方法了)

【代码】

#include<iostream>
#include<cstring>
#include<cstdio>
#define inf 2100000000
using namespace std;
int n,p,x,y,money,tmp,Min,k,m,nu,ans;
char ch;
struct hp{
	int p1,p2,cnt;
}a[1005];
bool pd;
int f[10000005],num[10000005];
int main(){
	freopen("magic.in","r",stdin);
	freopen("magic.out","w",stdout);
	scanf("%d%d",&n,&p);
	for (int i=1;i<=n;++i){
		scanf("%d",&x);
		ch=getchar();
		if (ch!=' ') money+=x;
		else{
			scanf("%d",&y);
			if (y-x<=p) money+=x;
			else a[++tmp].p1=x,a[tmp].p2=y,a[tmp].cnt=y-x-p,m+=x,k+=y;
		}
	}
	if (money>=p){
		money=money-p*tmp+k;
		printf("%d\n",money);
		return 0;
	}
	memset(f,127/3,sizeof(f));
	f[0]=0;num[0]=0;
	for (int i=1;i<=tmp;++i)
	  for (int v=m;v>=a[i].p1;--v){
	    f[v]=min(f[v],f[v-a[i].p1]+a[i].cnt);
	    if (v>=p-money&&f[v]!=707406378)
	      ans=max(ans,money+k-(f[v]+p*tmp));
	  }
	if (ans==0) ans=money+m;
	printf("%d\n",ans);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值