【237题】算法基础精选题单 刷题笔记DAY3 二分答案 下

[NOIP2012]借教室(二分答案与差分)

链接:https://ac.nowcoder.com/acm/problem/16564
来源:牛客网

首先考虑的肯定是暴力模拟,然后判断是否在某天存在教室数量小于0的情况,则为不满足
但复杂度为n2,超时

观察到对于每个订单的可满足性具有单调且有界性,即某一个订单为不满足为界,则其后面全不可满足,前面为可满足,那么就确定二分对象为订单,而对于区间操作我们可以利用差分

#include <iostream>
#include <algorithm>
using namespace std;
#define int long long

const int N = 2e6 + 10;
class node
{
  public:
  int need;
  int l,r;
};
int n, m;
int s[N];
int d[N];
node p[N];


int check(int mid)
{
  for(int i=0;i<=n;i++) d[i]=0;
  for(int i=1;i<=n;i++)
  {
     d[i]=s[i]-s[i-1];
  }
  for(int i=1;i<=mid;i++)
  {
    int l = p[i].l;
    int r = p[i].r;
    int w=p[i].need;
    d[l]-=w;
    d[r+1]+=w;
  }
  for(int i=1;i<=n;i++)
  {
    d[i]=d[i]+d[i-1];
    if(d[i]<0) return false;
  }
  return true;
  

}

signed main()
{
  int ans=0;
  cin>>n>>m;
  for(int i=1;i<=n;i++)
  {
     cin>>s[i];
  }
  for(int i=1;i<=m;i++)
  {
    int a,b,c;cin>>a>>b>>c;
       p[i]={a,b,c};
  }
  int l=1;int r=m+1;
  while(l<r)
  {
     int mid=(l+r)>>1;
     if(!check(mid))r=mid;
     else l=mid+1 ;
  }
  if(l==m+1) cout<<0<<endl;
  else 
  cout<<-1<<endl<<l<<endl;

}

NC14301 K-th Number

链接:https://ac.nowcoder.com/acm/problem/14301
来源:牛客网

一眼顶针,毫无头绪() ()即使知道是二分答案加尺取也不知道怎么写,看了题解才有大致的思路
首先明确两个性质:

  1. 对于已经存在的第k大的数,在添加进一个小于他的数后,其位置不发生改变
  2. 而在添加进一个大于他的数后,其位置变为k-i,i为大于他的数的数量
    条件1:其次明确,所有存在于B数组中的数都是由a中取得的,且B中必定存在m个数大于答案
    条件2:于是易得这m个数也是通过题目要求的k区间第k大数取得的,很容易联想到我们需要枚举区间,区间满足我在区间内取到的数字到B中必定能大于答案,且恰好有m个区间(因为在B中为第m大的数)
    思路:那么就可以二分猜测答案,那么怎么check呢,可以利用尺取法枚举区间,首先区间要满足第k大的数字大于等于我的猜测答案(区间内取数才能满足条件2),然后记录有多少个这样的区间,若区间数量大于m,那么可以增大答案,减少区间数量看是否满足,反之亦然,直到得到解。

代码如下

#include <iostream>
#include <algorithm>
using namespace std;
#define int long long

const int N = 2e6 + 10;
const int INF = 1e9;
int n, k, m;
int s[N];
int answer;

int check(int mid)
{
  int cnt = 0;
  int ans = 0;
  int j = 1;
  for (int i = 1; i <= n; i++)
  {
    if (s[i] >= mid)
      cnt++;
    if (cnt == k)
    {
      ans += n - i + 1;
      while (j <= i && s[j] < mid)
        ans += n - i + 1, j++;
      j++;
      cnt--;
    }
    else
      continue; // 统计区间数量
  }
  if (ans >= m)
    return true;
  else
    return false;
}

signed main()
{
  int temp;
  cin >> temp;
  while (temp--)
  {
    cin >> n >> k >> m;
    for (int i = 1; i <= n; i++)
      cin >> s[i];
    int l = 0;
    int r = INF;
    while (l < r)
    {
      int mid = (l + r+1) >> 1;
      if (check(mid))
        l = mid;
      else
        r = mid - 1;
    }
    cout << l << endl;
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值