CF Good Bye 2015 E. New Year and Three Musketeers(贪心+枚举)

4 篇文章 0 订阅
2 篇文章 0 订阅

题目链接:http://codeforces.com/contest/611/problem/E

题意:有三个抢手能力值分别为a,b,c。另外有n个小怪抵抗力为t(i)。当一个抢手能能力值大于等于小怪的抵抗力时抢手可以在一回合内杀死小怪。多个抢手同时联手当其能力值和大于等于某小怪抵抗力时可以再一回合内杀死小怪。问三个抢手最少多少回合能将小怪全部杀死。

解法:先贪心,后枚举。

1.贪心部分:把抢手能力值排序使a<=b<=c,把怪物按抵抗力从低到高排序,看是否a+b+c能消灭所有怪物,不能就输出“-1”。如果能,看是否有必要使用a+b+c。然后看是否有必要使用b+c,每使用一次b+c时,可以使用一次a(使用时发挥a的最大效能)。然后看是否有必要使用a+c,没使用一次a+c,可以使用一次b(发挥最大效能)。贪心部分的回合数为ans。

2.枚举部分:下面就面临着一个问题,如何使用a+b和c,假设a+b能消灭剩余的怪中的x个,c能消灭y个,那么如果只用a+b和c最快可以再max((x+y)/2,y-x,x-y)回合数内消灭全部怪物。那么我们就枚举只单独使用a、b、c的回合数,可以得到使用 a 或 b 或 c 或 a+b 时消灭怪物的最少次数,记为add。

最终的答案就是ans+add。(枚举部分需要体会理解,表示智商低,想了一天才想明白)。

3.实现时的技巧:将小怪抵抗力全部插入一个multiset中,需要查询小于等于某个值的最大抵抗力,而STL中的lower_bound(val)返回的是左开右闭范围中大于等于val的元素的位置,如果范围内全部值都小于val,则返回尾部空指针。这就需要我们在处理抵抗力时将所有抵抗力减一再插入multiset中,这样的好处是查询时只需要查询小于某个值的最大抵抗力,就可以直接使用lower_bound(val)而不需要判断得到的值是否等于val,可以大大减少代码量。

下付两种实现方法,大家可以体会:

1.抵抗力减一再插入:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <vector>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <iterator>
using namespace std;

multiset<int>  kill;
int w[3];

multiset<int>::iterator it,it2;

int ans;
void greedy(int dl,int pre){
    while(kill.size()!=0){
        it=kill.end();
        it--;
        if(*it<dl) break;
        kill.erase(it);
        ans++;
        it=kill.lower_bound(pre);
        if(it!=kill.begin()){
            it--;
            kill.erase(it);
        }
    }
}

int main (){
    int n;
    int a,b,c;
    while(scanf("%d",&n)!=EOF){
        scanf("%d%d%d",&w[0],&w[1],&w[2]);
        sort(w,w+3);
        kill.clear();
        int x;
        for(int i=1;i<=n;i++){
            scanf("%d",&x);
            x--;
            kill.insert(x);
        }
        it=kill.end();
        it--;
        if(*it>=w[0]+w[1]+w[2]){
            printf("-1\n");
            continue;
        }
        ans=0;
        greedy(w[1]+w[2],0);
        greedy(w[0]+w[2],w[0]);
        greedy(max(w[0]+w[1],w[2]),w[1]);
        int one=0,two=0;
        it=kill.begin();
        while(it!=kill.end()){
            if(*it<w[0]+w[1])  two++;
            if(*it<w[2])    one++;
            it++;
        }
        int add=n*10;
        for(int i=0;i<=n;i++){
            if(2*min(one,two)>=kill.size()){
                add=min(add,i+((int)kill.size()+1)/2);
            }
            else{
                add=min(add,i+(int)kill.size()-min(one,two));
            }
            for(int j=0;j<=2;j++){
                it2=kill.lower_bound(w[j]);
                if(it2!=kill.begin()){
                    it2--;
                    if(*it2<w[0]+w[1])  two--;
                    if(*it2<w[2])       one--;
                    kill.erase(it2);
                }
            }
        }
        printf("%d\n",ans+add);
    }
    return 0;
}

2.抵抗力直接插入:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <vector>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <iterator>
using namespace std;

multiset<int>  kill;
int w[3];

multiset<int>::iterator it,it2;

int ans;
void greedy(int dl,int pre){
    while(kill.size()!=0){
        it=kill.end();
        it--;
        if(*it<=dl) break;
        kill.erase(it);
        ans++;
        if(!kill.empty()){
            it=kill.lower_bound(pre);
            if(kill.size()==1){
                if(it==kill.end()){
                    it--;
                    kill.erase(it);
                }
                else
                    if(*it<=pre)
                        kill.erase(it);
            }
            else{
                if(it!=kill.end()&&*it<=pre)
                    kill.erase(it);
                else{
                    if(it!=kill.begin()){
                        it--;
                        kill.erase(it);
                    }
                }
            }
        }

    }
}

int main (){
    int n;
    int a,b,c;
    while(scanf("%d",&n)!=EOF){
        scanf("%d%d%d",&w[0],&w[1],&w[2]);
        sort(w,w+3);
        kill.clear();
        int x;
        for(int i=1;i<=n;i++){
            scanf("%d",&x);
            kill.insert(x);
        }
        it=kill.end();
        it--;
        if(*it>w[0]+w[1]+w[2]){
            printf("-1\n");
            continue;
        }
        ans=0;
        greedy(w[1]+w[2],0);
        greedy(w[0]+w[2],w[0]);
        greedy(max(w[0]+w[1],w[2]),w[1]);
        int one=0,two=0;
        it=kill.begin();
        while(it!=kill.end()){
            if(*it<=w[0]+w[1])  two++;
            if(*it<=w[2])    one++;
            it++;
        }
        int add=n*10;
        for(int i=0;i<=n;i++){
            if(2*min(one,two)>=kill.size()){
                add=min(add,i+((int)kill.size()+1)/2);
            }
            else{
                add=min(add,i+(int)kill.size()-min(one,two));
            }
            for(int j=0;j<=2;j++){
                if(!kill.empty()){
                    it2=kill.lower_bound(w[j]);
                    if(kill.size()==1){
                        if(it2==kill.end()){
                            it2--;
                            if(*it2<=w[0]+w[1])  two--;
                            if(*it2<=w[2])       one--;
                            kill.erase(it2);
                        }
                        else{
                            if(*it2<=w[j]){
                                if(*it2<=w[0]+w[1])  two--;
                                if(*it2<=w[2])       one--;
                                kill.erase(it2);
                            }
                        }
                    }
                    else{
                        if(it2!=kill.end()&&*it2<=w[j]){
                            if(*it2<=w[0]+w[1])  two--;
                            if(*it2<=w[2])       one--;
                            kill.erase(it2);
                        }
                        else{
                            if(it2!=kill.begin()){
                                it2--;
                                if(*it2<=w[0]+w[1])  two--;
                                if(*it2<=w[2])       one--;
                                kill.erase(it2);
                            }
                        }
                    }
                }
            }
        }
        printf("%d\n",ans+add);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值