1. 程式人生 > >JZOJ5937. 【NOIP2018模擬10.30】斬殺計劃

JZOJ5937. 【NOIP2018模擬10.30】斬殺計劃

Description

小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在配置腐敗藥水時會花費很大精力,他需要知道自己最少使用多少腐敗藥水,並在腐敗藥水數量最小的情況下花費最小的法力值

Input

第一行兩個正整數n,m表示小G的小弟數量和血量
第二行n個正整數表示小G所有小弟的攻擊力

Output

一行兩個整數表示最小的腐敗藥水數量和在腐敗藥水最小的情況下法力值花費,如果無論如何都無法戰勝,輸出一個整數-1

Data Constraint

測試點1,2: n≤10並且最優情況不需要使用腐敗藥水和獵人藥水
測試點3,4: n≤10並且最有情況不需要使用腐敗藥水
測試點5,6,7: n≤10
測試點8,9,10: n≤5000000,最大攻擊力小於等於30000
對於所有資料 0≤m≤5000000

題解

因為要使用的腐敗藥水最少,
所以先優先考慮如何用最小的腐敗藥水。
就是列舉腐敗藥水的數量,
能拉的小弟就拉過來,當第一次超過m以後,
也算是說當前就是腐敗藥水的最小值。
接下來就是要考慮最小的法力值了,
顯然,獵奇藥水是不值得的,貪心先去掉所以需要獵奇藥水的,但必須保證去掉之後依然是超過m的。
接下來,在複製型藥水中,相比能力值為2的小弟,能力值為1的小弟更加不值錢,
於是呢,下一個去掉能力值為1的小弟。
最後如果還有多的,才考慮去掉能力值為2的小弟。

code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#define N 30008
#define G getchar
using namespace std;
char ch;
void read(int &n)
{
	n=0;
	ch=G();
	while((ch<'0' || ch>'9') && ch!='-')ch=G();
	int w=1;
	if(ch=='-')w=-1,ch=G();
	while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
	n*=w;
}

int n,m,x,t[N],sum,s[3],ans;

int main()
{
	freopen("zhanshajihua.in","r",stdin);
	freopen("zhanshajihua.out","w",stdout);
	
	read(n);read(m);ans=-1;
	for(int i=1;i<=n;i++)read(x),t[x]++;
	for(int i=0;i<=10000;i++)
	{
		x=i*3;s[0]=s[0]+t[x+1];s[1]=s[1]+t[x+2];s[2]=s[2]+t[x+3];
		sum=s[0]+s[1]*2+s[2]*3;
		if(sum>=m)
		{
			printf("%d ",i);ans=i;
			break;
		}
	}
	if(ans<0)
	{
		printf("-1"); 
		return 0;
	}
	for(;sum-3>=m && s[2];s[2]--)sum=sum-3;
	for(;sum-1>=m && s[0];s[0]--)sum=sum-1;
	for(;sum-2>=m && s[1];s[1]--)sum=sum-2;
	ans=ans+s[2]*4+s[1]+s[0];
	printf("%d\n",ans);
	return 0;
}