题目背景
@Yumis 出题人在这里哦~
题目描述
Yumis最近在玩炉石传说。
在炉石传说中脏牧有一张一费卡片(一费就是使用要消耗1点法力水晶),叫做疯狂药水,这个的效果是将一个敌方场上攻击小于等于2的随从拉到自己的战场内。
还有一张四费卡片叫做暗影狂乱,这个的效果是将一个敌方场上攻击小于等于3的随从拉到自己的战场内。
还有一张一费卡片就是缩小药水,这个的效果是将敌人全场的随从攻击力下降3点。
你PY了炉石的GM所以你有了无数张的这三种卡片,但是GM告诉你缩小药水是这个牌比较不好创建,为了为GM着想你必须在使用最少的缩小药水的情况下A爆对手的脸。
现在你的对手场上有n个随从,每个随从的攻击力是ki点。
你的对手有m点血量。
而你现在要做的就是将敌方的场上的随从拉过来自己的场上并攻击对手(每一个随从只能攻击一次,攻击力为你拉过来的时候随从剩余的攻击力),A爆对面的脸(将对面的血打到0点及以下)。
输入输出格式
输入格式:
第一行用一个空格隔开的两个整数n,m分别代表敌方场上的随从数量和你对手的血量。
第二行n个整数每两个整数之间用一个空格隔开,分别代表敌方场上每一个随从的攻击力ki。
输出格式:
一行如果可以A爆则输出最少使用的缩小药水的数量和此时使用的法力水晶,两个数据之间用一个空格隔开(如果有多个答案则输出消耗法力水晶最少的答案)。
否则输出“Human Cannot Win Dog”(没有双引号)
输入输出样例
说明
样例说明1:
敌方场上有3只随从,敌方有5点血量
我们把3攻随从和2攻随从拉过来花费0个缩小药水和5点耗费(一张疯狂药水一个暗影狂乱(1+4 = 5))伤害足够击杀对方。
样例说明2:
使用16个缩小药水(下面数据后面第一个括号指拉过来的时候伤害为多高 ,第二个括号表示拉过来的时候使用多少的缩小药水)
拿10(1)(3)、20(2)(6)、30(3)(9)、50(2)(16)攻的怪物总共造成8点伤害 刚好A爆!
Easy : 保证 0 < n <= 10 并且不存在用到暗影狂乱和缩小药水的情况 20%
Normal :保证 0 < n <= 10 并且不存在用到缩小药水的情况 20%
Hard :保证 0 < n <= 10 30%
Extra:保证0 <= n <= 5000000(6个0),最大攻击力小于30000 30%
保证 0 < n <= 5000000 0 < ki <=30000 0<=m<=5000000 (6个0)
感受:一开始做这道题的时候真的是一点思路都没有,又懒得打搜索
所以直接输出了无解情况,10分
正解:转载的https://www.luogu.org/blog/user51208/solution-p3944
对于这道题来说
Easy和normal:随便搜索都可以过的啊这个不说了啊
Hard:你可以用先用sort排序一遍然后依次记录使用每一瓶缩小药水最大打多少然后再用Easy和normal的方式搜索这一层就够。
Extra:根据Hard的想法我们可以很清晰的得到我们是要搜索使用每一瓶缩小药水最大能打多少伤害,而且最大的伤害量也不大,那么可以用到一个很冷门的排序方式 桶排序 就是输入的时候直接把全部的数据读入然后用桶读入
接下去用for循环每次给i增加3计算到 i = 29998就可以
然后每丢一个缩小药水就计算一次如果攻击大于m则跳出循环
然后用3个while判断
先把3攻的随从去掉 其次是1攻的最后是2攻的
这样答案就很明显出来了
详见代码
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cstring> 5 #include<string> 6 #include<cmath> 7 #define ll long long 8 #define DB double 9 #define inf 214748360000 10 using namespace std; 11 inline int read() 12 { 13 int x=0,w=1;char ch=getchar(); 14 while(!isdigit(ch)){if(ch==‘-‘) w=-1;ch=getchar();} 15 while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-‘0‘,ch=getchar(); 16 return x*w; 17 } 18 int n,m,ans,num=-1,at1,at2,at3,t[3000000]; 19 //ans 表示造成的伤害 20 //num 记录使用多少缩小药水 21 //at1 at2 at3 用来记录你拉了多少个可以造成多少伤害的随从 22 int main() 23 { 24 n=read();m=read(); 25 for(int i=1;i<=n;++i) 26 { 27 int p;p=read(); 28 t[p]++; 29 }//读入数据然后桶排 30 for(int i=1;i+2<=30001;i+=3) 31 { 32 ans+=t[i];at1+=t[i];//桶内有多少个可以打出1点伤害的 33 ans+=t[i+1]*2;at2+=t[i+1];//桶内有多少个可以打出2点伤害的 34 ans+=t[i+2]*3;at3+=t[i+2];//桶内有多少个可以打出3点伤害的 35 num++;//多使用一瓶缩小药水 第一次进入的时候是不使用 36 if(ans>=m) break;//如果伤害大于则退出循环寻找 37 } 38 while(ans>=m+3 && at3) at3--,ans-=3; 39 //如果伤害溢出超过3点则先把3攻随从去掉 因为3攻需要用到4费显然是十分不值得 40 while(ans>=m+1 && at1) at1--,ans-=1; 41 //其次是删除掉1点攻击力的,因为同样的耗费显然是2攻打得多 42 while(ans>=m+2 && at2) at2--,ans-=2; 43 if(ans<m) cout<<"Human Cannot Win Dog"; 44 else cout<<num<<" "<<at1+at2+at3*4+num; 45 return 0; 46 }
我想做个高冷的人呢。