1. 程式人生 > >jzoj5937 斬殺計劃 貪心

jzoj5937 斬殺計劃 貪心

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; }