cf822C(贪心)

题目链接: http://codeforces.com/problemset/problem/822/C

 

题意: 有n条线段(n<=2e5) 每条线段有左端点li,右端点ri,价值cost(1 <= li <= ri <= 2e5, cost <= 1e9);

对于一个给定的x(x <= 2e5),寻找两个不相交的线段,使它们的长度和恰好为x,并且价值和最小;

 

思路: xjb贪心

这题目肯定是枚举一条线段再找剩下一条线段, 假设枚举右线段, 且其长为len1, 那么我们需要找这条线段左边的长度为 x - len1 的线段的 cost 最小值;直接暴力的话是n^2的复杂度, 肯定 tle. 可以给这些线段按照左右端点排下序, 那么我们可以从按左端点排序的数组中枚举右线段, 然后在按右端点排序的数组中找左线段;但是如果暴力找的话时间复杂度还是 n^2 . 我们可以维护一个 vis数组, vis[i] 为当前右线段的左边线段中长度为 i 的 cost 最小值, 那么 vis[x - len1] 即为当前左线段的 cost 最小值;

那么 cost1 + vis[x - len1] 的最小值即为答案啦;

 

代码:

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string.h>
 4 #include <algorithm>
 5 #define ll long long
 6 using namespace std;
 7 
 8 const int MAXN = 2e5 + 10;
 9 const int inf = 0x7f7f7f7f;
10 ll vis[MAXN];
11 struct node{
12     int l, r;
13     ll w;
14 }gel1[MAXN], gel2[MAXN];
15 
16 bool cmp1(node a, node b){
17     return a.l < b.l;
18 }
19 
20 bool cmp2(node a, node b){
21     return a.r < b.r;
22 }
23 
24 int main(void){
25     int n, x;
26     ll ans = inf;
27     scanf("%d%d", &n, &x);
28     for(int i = 0; i < n; i++){
29         scanf("%d%d%lld", &gel1[i].l, &gel1[i].r, &gel1[i].w);
30         gel2[i] = gel1[i];
31     }
32     sort(gel1, gel1 + n, cmp1);
33     sort(gel2, gel2 + n, cmp2);
34     memset(vis, 0x7f, sizeof(vis));
35     int indx = 0;
36     for(int i = 0; i < n; i++){
37         ll gg = gel1[i].w;
38         while(indx < n && gel2[indx].r < gel1[i].l){
39             int cnt = gel2[indx].r - gel2[indx].l + 1;
40             if(vis[cnt] > gel2[indx].w) vis[cnt] = gel2[indx].w;
41             indx++;
42         }
43         int cc = x - (gel1[i].r - gel1[i].l + 1);
44         if(cc <= 0) continue;
45         gg += vis[cc];
46         if(ans > gg) ans = gg;
47     }
48     if(ans == inf) cout << -1 << endl;
49     else cout << ans << endl;
50     return 0;
51 }
View Code

 

转载于:https://www.cnblogs.com/geloutingyu/p/7116392.html

08-02
<think>嗯,用户想找Codeforces 1380C题目的解法。这道题在Codeforces上属于常规比赛题目,考察的主要是贪心算法和排序技巧。 用户可能是正在备赛的选手,需要快速理解题目解法。从问题表述看,用户直接要solution,说明ta更关注实现而非题目描述,因此回复要精简,核心是算法思路和代码实现。 这道题的关键点在于:题目要求将数组分成若干子组,每个子组满足"组内最小值×组大小≥给定值x"。最优策略是倒序排序后,用贪心法尽可能多地形成大组,因为小数在末尾不会拉低大组的乘积。 准备用标准解法:先排序,然后从最大值开始遍历,动态维护当前组的大小和最小值。当满足条件时立即分组。这样时间复杂度是O(n log n),主要来自排序。 需要特别注意边界情况:当所有元素都小于x时,可能每个元素单独成组;当x很大时,可能无法分组。这些在代码中要处理。 用户可能还会问时间复杂度的证明或贪心正确性,所以相关问题可以设计这两个方向。另外考虑到实际编码常见错误,准备添加一个关于"常见错误"的问题提示。</think>以下是Codeforces 1380C(Create The Teams)的题解,基于贪心算法实现: ### 题目核心 给定长度为 $n$ 的数组 $a$ 和整数 $x$,需将数组分成若干子组,使得每组满足: $$ \text{组内最小值} \times \text{组大小} \geq x $$ 求最大分组数量。 ### 贪心策略 1. **排序与倒序遍历** - 将数组 **降序排序**(从大到小),使较大元素优先成组 - 倒序遍历可避免小数拉低组内最小值 2. **动态维护分组** - 用 $current\_min$ 记录当前组的最小值(即最新加入的元素) - 用 $count$ 记录当前组大小 - 当满足 $current\_min \times count \geq x$ 时立即分组 ### 代码实现 ```python t = int(input().strip()) results = [] for _ in range(t): n, x = map(int, input().split()) a = list(map(int, input().split())) a.sort(reverse=True) # 降序排序 groups = 0 current_min = float('inf') count = 0 for num in a: current_min = min(current_min, num) # 更新组内最小值 count += 1 # 组大小+1 if current_min * count >= x: # 满足条件时成组 groups += 1 current_min = float('inf') count = 0 results.append(groups) for res in results: print(res) ``` ### 算法正确性证明 - **贪心选择有效性**:较大元素优先成组可最大化利用其值(因 $current\_min$ 由最小元素决定,大元素不会降低该值)[^1] - **时间效率**:排序 $O(n \log n)$ + 遍历 $O(n)$,总复杂度 $O(n \log n)$ ### 示例分析 输入:`a = [2, 5, 3, 1], x = 6` 1. 降序排序:`[5, 3, 2, 1]` 2. 遍历过程: - 第一组:`[5]` → $5 \times 1 = 5 < 6$(继续) `[5,3]` → $\min(5,3) \times 2 = 3 \times 2 = 6 \geq 6$ → 成组 - 第二组:`[2]` → $2 \times 1 = 2 < 6$(继续) `[2,1]` → $\min(2,1) \times 2 = 1 \times 2 = 2 < 6$ → 无法成组 3. 输出:`1`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值