对抗女巫的魔法碎片(贪心,排序)

对抗女巫的魔法碎片

Problem Description

光明世界的一个国家发生动荡,女巫利用了邪恶的力量将国家的村庄都施下了咒语,好在国家还有英勇 的士兵,他们正义的力量能够破解这些魔咒夺回村庄,并且得到魔法碎片,利用足够多的魔法碎片可以将女巫铲除。
现在己经被魔咒封印的村庄有m个,编号为1到m。英勇的士兵n个,编号从1到n。第i个士兵攻击力 为ai,第j个村庄防御力为bj,魔法价值为cj。
现在这些士兵想夺回这些村庄,每个士兵可以最多占领一个村庄,一个村庄最多被一个士兵占领。当士兵的攻击力ai大于村庄的防御力bj的时候,该士兵就可以夺回这个村庄,并且士兵会获得魔法碎片ai−bj+cj 个。
现在想知道这些士兵夺回村庄,获得的魔法碎片之和最多是多少

Input

输入第一行一个整数T,表示有T组数据。
接下来一行输入两个整数n和m。
接下来一行,输入n个数ai,表示士兵的攻击力。
接下来m行,每行输入两个数bi,ci,表示村庄的防御力和该村庄的魔法价值。
1 <= n,m <= 100000
1 <= ai,bi,ci <= 100000

Output

一个整数,表示获得的魔法碎片的数量

Sample Input

2
3 3
4 4 4
2 3
1 3
5 3
3 3
4 4 6
2 3
4 3
5 3

Sample Output

11
10

思路:

  • 参考了对抗女巫的魔法碎片 文章

  • 感觉是是贪心,先按照村庄的实际价值(本身价值-防御力)降序排序,那么我们就想从前到后的尽可能多的攻打这些村庄。

  • 但是因为士兵是有攻击力这么一说的,所以我们要尽可能的让刚好大于村庄防御力的士兵去攻打,每次都妄图以最小的代价去获得最大的利益。这样,获取的实际利益最大并且攻打的数量也是最多。

  • 可以用multiset存士兵的攻击,然后用upper_bound()去找刚好大于防御力的某个士兵。

  • 最后我们会的到能攻打的村庄的数量,虽然我们在过程中是用最小攻击力的士兵攻打,但是题目中的利益获得是士兵的攻击力高也就越高,所以我们得到数量后,用攻击力高的士兵去算利益。

AC代码

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<set>
    #include<cstring>
    using namespace std;

    const int maxn = 100010;
    int a[maxn];               //士兵
    int b[maxn];               //村庄防御 
    int c[maxn];               //村庄价值 
    int d[maxn];               //村庄的索引 添加的时候是从0到m-1 但是会排序 拍的序列就放到d 

    bool cmp(int i,int j)
    {
        return c[i] - b[i] > c[j] - b[j];  //价值减去防御的值来排序    
    }

    int main()
    {
        int T;
        scanf("%d",&T);
        multiset<int > s;

        while(T--)
        {
            s.clear();
            memset(a,0,sizeof(a));

            int n,m;
            scanf("%d %d",&n,&m);

            for(int i=0;i<n;i++)
            {
                scanf("%d",&a[i]);
                //同时按照从小到大的顺序插入multiset
                s.insert(a[i]); 
            }


            for(int i=0;i<m;i++)
            {
                scanf("%d %d",&b[i],&c[i]);
                d[i] = i;                         //默认村庄排序从0到m-1 
            }   

            sort(a,a+n,greater<int>());           //士兵先按照降序排列
            sort(d,d+m,cmp); 

            //从攻打村庄的价值 从高到低看能攻打多少个
            int cnt = 0;
            long long ans = 0; 

            multiset<int >::iterator it;
            for(int i = 0; i < m; i++)
            {
                int id = d[i];            //通过d的索引到最大价值的村庄去  因为d是索引按照最大价值排序
                it = s.upper_bound(b[id]);
                if(it == s.end())  
                    continue;             //妄图以最小代价去获得最大价值 打不下来这个村庄 

                cnt ++;
                ans += c[id] - b[id];     //攻打村庄后的价值 不算士兵的 
                s.erase(it);
            } 

            for(int i=0;i<cnt && i<m;i++)
                ans += a[i];               //得到我们最多能攻打的村庄数量,然后让攻击力最高的去攻打,获得最大利益

            printf("%lld\n",ans); 
        }   
        return 0;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值