Odd-Even Subsequence 解题报告

本文解析了Codeforces竞赛中一道关于寻找子序列最小成本的问题,通过二分查找和贪心策略,实现了一种有效的解决方案。

题目链接:https://codeforces.ml/contest/1370/problem/D

Ashish has an array a a a of size n n n.

A subsequence of a a a is defined as a sequence that can be obtained from a a a by deleting some elements (possibly none), without changing the order of the remaining elements.Consider a subsequence s s s of a a a. He defines the cost of s s s as the minimum between:

  • The maximum among all elements at odd indices of s s s.
  • The maximum among all elements at even indices of s s s.

Note that the index of an element is its index in s s s, rather than its index in a a a. The positions are numbered from 1 1 1. So, the cost of s s s is equal to m i n ( m a x ( s 1 , s 3 , s 5 , … ) , m a x ( s 2 , s 4 , s 6 , … ) ) min(max(s1,s3,s5,…),max(s2,s4,s6,…)) min(max(s1,s3,s5,),max(s2,s4,s6,)).

For example, the cost of { 7 , 5 , 6 } \left\{7,5,6\right\} {7,5,6} is m i n ( m a x ( 7 , 6 ) , m a x ( 5 ) ) = m i n ( 7 , 5 ) = 5 min(max(7,6),max(5))=min(7,5)=5 min(max(7,6),max(5))=min(7,5)=5.

Help him find the minimum cost of a subsequence of size k k k.

比较明显能看得出思路是二分答案,然后一种思路是将数列排序后取若干相间隔的数,但这样的话会有个问题:数列两端点的数取与不取的情况很难讨论。

因此考虑另一种思路:顺序取数,用一个bool变量记录当前取的是奇数位还是偶数位,对于奇数位/偶数位取数时保证其满足小于等于mid,对于偶数位/奇数位则无限制条件。这种贪心思路为什么成立呢?

假设我们遇到了一个满足条件的数,它的下一个数也满足条件,那这时应该取哪个数呢?这个就是贪心的核心思想,显然,取的数越前越好,因此直接取当前数即可。

代码:

#include<cstdio>
#include<algorithm>
using namespace std;
 struct newdata
 {
  int label;
  long long x;
 };
 newdata a[200005];
 int b[200005];
 int n,k;
bool cmp(newdata i,newdata j)
{
 return i.x < j.x;
}
int main()
{
 scanf("%d%d",&n,&k);
 for (int i = 1;i <= n;i ++)
 {
  a[i].label = i;
  scanf("%I64d",&a[i].x);
  b[i] = a[i].x;
 }
 sort(a + 1,a + n + 1,cmp);
 int l = 1;
 int r = n;
 while (l < r)
 {
  int mid = l + r >> 1;
  int cnt = 0;
  bool now = false;
  for (int i = 1;i <= n;i ++)
   if (now)
   {
    cnt ++;
    now ^= 1;
   }
   else
   {
    if (b[i] <= a[mid].x)
    {
     cnt ++;
     now ^= 1;
    }
   }
  if (cnt >= k)
  {
   r = mid;
   continue;
  }
  cnt = 0;
  now = true;
  for (int i = 1;i <= n;i ++)
   if (now)
   {
    cnt ++;
    now ^= 1;
   }
   else
   {
    if (b[i] <= a[mid].x)
    {
     cnt ++;
     now ^= 1;
    }
   }
  if (cnt >= k)
   r = mid;
  else l = mid + 1;
 }
 printf("%d",a[l].x);
 return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值