hdu4415 Assassin’s Creed

原题: http://acm.hdu.edu.cn/showproblem.php?pid=4415

题目大意:EA手上有一把武器,耐久度为m,现在他知道前面有n个敌人,杀死第i个敌人ei会降低武器的耐久度ai,但是杀死这个敌人ei可以额外杀死任意bi个人,而杀死这bi个人不降低耐久度 ,问 在保证杀死最多人的情况下,最少需要消耗多少耐久度?  输出最多杀人数和消耗最少的耐久度
贪心策略: 先清楚一点:敌人分为两类,第一类:bi=0的敌人; 第二类:bi>0的敌人,对于bi>0的这类敌人,只要杀了其中一个,其余bi>0的敌人都可以被自动杀死
          方案一:手动杀死只杀bi==0的敌人,先把bi=0的敌人按消耗值从小到大排序,然后算出最大杀敌数和被消耗的耐久度;
          方案二:先杀死一个bi>0且ai最小的敌人,扣除ai耐久度后,我们可以知道其余bi>0的敌人全都会死,可能还可以附带自动杀死一些bi=0的敌人,然后我们再利用剩下的耐久度去手动杀bi=0的敌人;
          但是方案二有一种情况要考虑,如果之后我们利用剩余的耐久度 去 手动杀死一个bi=0的敌人e1,但是e1的损耗值ai很大,但其实我们自己可以手动杀死一个bi>0而ai较小的敌人e2,本来e2是要被自动杀死的,所以我们应该把自动杀死的名额让给消耗值更大的e1  
          也就是说,先把能被自动杀死的人数求出来(即bi的和,这些人是一定会被自动死的),然后按消耗值从小到大排序,每次优先杀死消耗值小的敌人
          比如e1本来是被自动杀死的,并且我的武器耐久度也足够杀到他,但是我们在手动杀死bi=0的这类敌人的时候,发现有个敌人e2的消耗值远大于e1,那我们就选择手动杀死e1,然后让e2被自动杀死        
          最终结果就是方案一和方案二取最优解,注意一旦杀敌数>=n就可以结束循环,然后杀敌数取n,因为杀敌数不可能大于敌人总数。
如果还不明白可以阅读这位大神的解题报告: http://www.mamicode.com/info-detail-1032227.html

#include<iostream>
#include<cstdio>
#include<algorithm>
#define qwq 0x7fffffff
using namespace std;
struct Ea
{
	int ai;
	int bi;
}ea[100001],e[100001];//ea 存放b等于0的敌人,e存放全部敌人
int cmp(Ea a,Ea b)//按消耗值从小到大 
{
	return a.ai<b.ai;
}
int main()
{
	int t;
	int ca=0;
	scanf("%d",&t);//t组测试数据 
	while(t--)
	{
		ca++;
		int n,m;//敌人的数目,消耗度m 
		scanf("%d %d",&n,&m);
		int pos1=0;//ea的下标 
		int min=qwq;//b>0并且消耗值最小的敌人p的消耗值 
		int pos=0;//敌人p所在的下面 
		int sumbi=0;//自动杀敌数 
		for(int i=0;i<n;i++)
		{
			int a,b;
			scanf("%d %d",&a,&b);//血量a,消耗度b 
			if(b==0)
			{
				ea[pos1].ai=a;//没有炸弹的 
				pos1++;
			}else{//b>0
				sumbi=sumbi+b;//全部B相加,这些是可以被自动杀死的人数 
				if(a<min)
				{
					min=a;
					pos=i;
				} 
			}
			e[i].ai=a;
			e[i].bi=b;
		}
		//情况一,只杀b==0的 
		int m1=m;
		int num1=0;
		sort(ea,ea+pos1,cmp);//b==0的敌人按消耗值排序 
		for(int i=0;i<pos1;i++)
		{
			if(m1>=ea[i].ai)
			{
				num1++;
				m1=m1-ea[i].ai;
			}else{
				break;
			}
		}
		//情况二,先杀B>0的并且血最低的敌人p 
		int m2=m-min;//杀了p,扣消耗值 
		int num2=0;
		if(m2>=0)//表示可以杀掉那个人p
		{
			num2=sumbi+1; //杀掉p之后可以自动杀死的人数 
			e[pos].ai=-1;//代表p已经死了 
			sort(e,e+n,cmp);//按消耗值从小到大排序 
			for(int i=1;i<n;i++)//除了p,遍历每一个还活着的敌人 
			{
				if(num2>=n){//如果杀敌数>=总人数了,直接跳出 
					break;
				}
				if(m2>=e[i].ai)//遇到血量少于消耗值的,杀 
				{
					m2=m2-e[i].ai;				
					num2++;
				}else{
					break;
				}
			}
		}else{//杀不掉p 
			num2=-1; 
		}
		num2=num2>n?n:num2;//杀敌数不能大于n 
		if(num1>num2)//情况一的杀敌数比较多 
		{
			printf("Case %d: %d %d\n",ca,num1,m-m1);
		}else if(num1<num2)//情况二的杀敌数比较多 
		{
			printf("Case %d: %d %d\n",ca,num2,m-m2);
		}else if(m1>m2){//两种情况的杀敌数相同,比较消耗值大小 
			printf("Case %d: %d %d\n",ca,num1,m-m1);
		}else{
			printf("Case %d: %d %d\n",ca,num2,m-m2);
		}
	}
	return 0; 
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值