jzoj5937 斩杀计划 贪心

版权声明:虽然是个蒟蒻但是转载还是要说一声的哟 https://blog.csdn.net/jpwang8/article/details/83549256

Description


众所周知,小J和小G是死对头,一天小G带领一群小弟找到了小J。
问题描述
小G有n个小弟,第i个小弟有ai点攻击力,小G有m点血量。
小J在小G找小第的时间里去找小Z学到了膜法,他在大战前配置了三种魔法药水
1:复用型药水:花费1法力值,选择小G的攻击力小于等于2的一个小弟让他跟随自己(变为自己的小弟并且攻击力和属于小G时一样)
2:猎人药水:花费4法力值,选择小G的攻击力小于等于3的一个小弟让他跟随自己
3:腐败药水:花费1法力值,使小G所有小弟攻击力降低三点(使用前两种魔法将小弟拉到自己阵营时小弟攻击力就是当前的攻击力,即小J的小弟攻击力只能为1,2,3)
为了向小G展现自己的力量,他打算在召集到一些小弟后发动攻击(每个小弟打一次)直接秒杀小G(攻击力大于等于m)
由于智商有限,小J在配置腐败药水时会花费很大精力,他需要知道自己最少使用多少腐败药水,并在腐败药水数量最小的情况下花费最小的法力值

数据规模和约定
测试点1,2: n≤10并且最优情况不需要使用腐败药水和猎人药水
测试点3,4: n≤10并且最有情况不需要使用腐败药水
测试点5,6,7: n≤10
测试点8,9,10: n≤5000000,最大攻击力小于等于30000
对于所有数据 0≤m≤5000000

Solution


最水的题目。。想麻烦了

考虑贪心。我们先枚举第一个答案,然后能装就装
由于要在第一个答案尽可能小的情况下求第二问,因此这样是最优的。
优先删去3(花费4法力显然不值),然后删去1(相比较而言肯定优于删去2),最后删去2

我的做法比较sb
第一问显然是可以二分的,我们二分第一问然后对可以选的物品做多重背包。用单调队列优化可以做到O(3mlog30000),开了O2卡一卡好像是可以过的

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)

const int N=5000005;

int a[N],r[N];

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

int main(void) {
	int n=read(),m=read();
	if (!m) return 0&puts("0 0");
	rep(i,1,n) {
		a[i]=read();
		++r[a[i]];
	}
	int sum=0,cnt1=0,cnt2=0,cnt3=0,ans=-1;
	rep(i,1,30000) {
		if (i%3==1) cnt1+=r[i],sum+=r[i];
		else if (i%3==2) cnt2+=r[i],sum+=2*r[i];
		else cnt3+=r[i],sum+=3*r[i];
		if (sum>=m) {
			ans=(i-1)/3;
			printf("%d ", ans);
			break;
		}
	}
	if (ans==-1) return 0&puts("-1");
	ans+=cnt1+cnt2+cnt3*4;
	int p=sum-m;
	if (p/3) {
		ans-=std:: min(p/3,cnt3)*4;
		p-=std:: min(p/3,cnt3)*3;
	}
	if (p) {
		ans-=std:: min(p,cnt1);
		p-=std:: min(p,cnt1);
	}
	if (p/2) ans-=std:: min(p/2,cnt2);
	printf("%d\n", ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/jpwang8/article/details/83549256