1 条题解
-
0
思路
题意:有 支敌军,第 支的到达时间为 、威胁值为 。最多组织 次拦截,每次只能拦截一支,且拦截时间 必须满足 。求被拦截敌军威胁值之和的最大值。
关键:时间约束不产生限制
每次行动的拦截时间可以自由指定,不同行动之间没有先后、数量或时间槽的限制。因此对任意挑选出的 支敌军,只需把第 支的拦截时间分别设为 (或任意不小于 的时刻),就能全部拦截成功——合法方案总是存在的。
换句话说,原问题里的“时间”条件对答案没有任何约束作用,任何大小不超过 的子集都是可行的。
转化为取前 k 大
既然可行性不受限制,要最大化总威胁值,就应当贪心地选威胁值最大的 支:
- 设所有 降序排列为 ,则答案就是 ;
- 最优性:任取一个 元子集,把它与“前 大”比较,若存在一个元素不在前 大中,则必有一个前 大的元素未被选入,用后者替换前者不会让总和变小,反复替换即得到前 大,故其和最大;
- 因为所有 ,多选不会变差,所以恰好选 支(而不是少于 支)总是最优的。
实现
读入 个数对后,按威胁值 从大到小排序(
t只读入、不参与计算),再把前 个的 累加输出。注意sort的比较函数只看v,时间字段被忽略。复杂度
每组数据时间 (排序),空间 。题目保证 ,排序足以通过。
代码
#include<bits/stdc++.h> #define int long long #define dd double #define str string #define el cout << '\n' #define re return #define ff first #define ss second #define pll pair<int,int> #define pdd pair<dd,dd> #define all(x) x.begin(),x.end() #define pb push_back using namespace std; struct p{ int t; // 到达时间(拦截时间可自由选择,故不参与计算) int v; // 威胁值 }; // 每组测试:读入敌军信息,输出最多 k 次拦截可获得的最大威胁值之和 void go(){ int n,k;cin>>n>>k; vector<p>a(n); for(int i=0;i<n;i++){ cin>>a[i].t>>a[i].v; } // 按威胁值从大到小排序,取前 k 大之和 sort(all(a), [](p a1, p a2){ return a1.v>a2.v; }); int sum = 0; // 被拦截敌军的威胁值总和 for(int i=0;i<k;i++){ sum += a[i].v; } cout<<sum;el; } signed main() { ios::sync_with_stdio(false); cin.tie(nullptr);cout.tie(nullptr); int _=1; cin >>_; // 读入测试组数 T while(_--){ go(); } return 0; }
- 1
信息
- ID
- 44
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 3
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者