周练回顾(4)

这个周复习了二分法,并做了洛谷上的一些题
个人看到的比较好的二分法模板文章:
https://blog.csdn.net/guslee/article/details/109222477?
P1824 进击的奶牛
给出n个牛棚和牛栏的位置,牛的个数c,尽量将牛放的距离比较远。输出将牛放置在牛棚里相邻牛距离的最近的最大值。
很经典的最小值最大问题,二分答案类的题目可以直接套用模板查找x,关键是写出judge函数来判断这个x可不可行。首先用牛棚的数量减去牛的数量,看看中间可以空出多少位置s。x为两牛距离的最近值,如果出现两牛棚之间的距离比这个最近值x还要小那么这个地方就要空出来不能放牛(否则它就不是最近值了)通过这样的道理看看空出了多少牛棚,如果大于s就说明空位太多,最近值大了,要把x变小,反之要变大。最后就是最小值最大了,我们要找到满足judge函数的最大值,不用急着打破while循环。

#include 

using namespace std;
typedef long long ll;
ll a[1000001];
ll s,u,i,n;
bool judge(ll x)
{   ll num=0;
    u=a[0];
    for(i=1;i<n;i++)
    {
        if(a[i]-u<x) num++; //小于最小值,这个位置不能放牛
        else u=a[i];  //大于最小值,计算下一个距离
        if(num>s) return false;
    }
    return true;
}
int main()
{
    ll c,l,r,mid,ans=0;
    cin>>n>>c;
    for(i=0;i<n;i++)
    {
        cin>>a[i];
    }
    sort(a,a+n);
    l=1;
    r=a[n-1]-a[0];
    s=n-c;
    while(l<=r)
    {
       mid=(l+r)/2;
       if(judge(mid))
       {
           l=mid+1;
           ans=max(ans,mid); //找到最近的最大值
       }
       else r=mid-1;
    }
    cout<<ans<<endl;
}

P1182 数列分段 Section II
给出一段数列,要将其分为m段,求分为m段每段和的最大值的最小值为多少
和上一题正好相反,但是意思几乎相同。构建judge函数,对于数列4 2 4 5 1,用while查找满足judge函数的x(即最大值)。每次大于最大值就要分段,求得分段数,如果分段数大于m,说明最大值小了,要把它变大。反之则反,要求最大值的最小值,不要着急返回。还有就是要注意特殊情况,本题使用二分法l的值应为数列中的最大的数,r应为数列的和,否则容易wr。

#include 
using namespace std;
typedef long long ll;
ll a[100001];
ll i,n,m;
ll num;
bool judge(ll x)
{   ll sumn=0,k;
    num=1;
    for(i=0;i<n;i++)
    {
        sumn=sumn+a[i]; 
        if(sumn>x) {sumn=a[i];num++;} //如果大于最大值就该分段了,前面这个数不能加
    }
    if(num<=m) return true;
    else return false;
}
int main()
{
    ll l,r,mid,ans=0,sum=0;
    cin>>n>>m;
    for(i=0;i<n;i++)
    {
        cin>>a[i];
        sum=sum+a[i];
        ans=max(ans,a[i]); //注意l不是0,否则会有一个点wr
    }
    l=ans;
    r=sum;
    while(l<=r)
    {
       mid=(l+r)/2;
       if(judge(mid))
       {
           r=mid-1;
           ans=mid;  //求最大值的最小满足情况
       }
       else l=mid+1;
    }
    cout<<ans<<endl;
    return 0;
}

P1843 奶牛晒衣服
晾晒n件衣服,每件衣服有自己的湿度,给出每小时风干的湿度a和烘衣机蒸干的湿度b,问最少要用多少个小时来风干衣服。
依旧要写judge函数,因为烘衣机每次只能烘干一件衣服。我们用二分法查找小时数x,在这x小时里如果被风干的衣服那确实就被风干了(你搁这搁这呢),如果没被风干就需要烘衣机来帮忙。但是对于x小时,如果能将剩余衣服烘干那么x小时成立,如果不能的话就要增大这个x了。

#include 

using namespace std;
int n,a,b,i;
int c[500001];
bool judge(int x)
{
    int k=1;
    int t=x;
    for(i=0;i<n;i++)
    {   int num=0;
        if(t<0) {k=0;break;}
        if(k==0) break;
        if(c[i]-a*x<=0) continue;  //被风干了
        else{
            num=num+a*x;
            if(c[i]-b*t-num>0) {k=0;break;}
            else{
                while(1)  //需要洗衣机烘多少小时才能烘干
                {
                    num=num+b;
                    t--;
                    if(t<0) {k=0;break;}
                    if(c[i]<=num) break;
                }
            }
        }
    }
    if(k==1) return true;
    if(k==0) return false;
}
int main()
{
    int ans=999999,mid;
    cin>>n>>a>>b;
    memset(c,0,sizeof(c));
    for(i=0;i<n;i++)
    {
        cin>>c[i];
    }
    sort(c,c+n);
    int l=1;int r=c[n-1]/a+1;
    while(l<=r)
    {
       mid=(l+r)/2;
       if(judge(mid)){
           r=mid-1;
           ans=min(ans,mid);  //求满足烘干的最短小时
       }
       else l=mid+1;
    }
    cout<<ans<<endl;
}

P1439 【模板】最长公共子序列
它的最大数据是10的五次方,而dp的复杂度为n的平方,打dp会超时。所以我们需要先用第一个串对第二个串进行离散化处理(我现在还是有点不懂)然后求离散化后串的最长上升子序列(求最长上升子序列有二分的做法,复杂度只需要nlog2n)这样就可以解决超时的问题了。
以下选取了洛谷题解中某个大佬的做法,我实在不会

#include
using namespace std;
const int maxv=2000100;
int a[maxv],b[maxv],f[maxv];
int n,ans=0;
int main()
{
 cin>>n;
 for(int i=1;i<=n;++i)
 {
  int k;
  cin>>k;
  b[k]=i;
 }
 for(int i=1;i<=n;++i)
 {
  int k;
  cin>>k;
  a[i]=b[k];  //离散化处理
 }
 for(int i=1;i<=n;++i)
 {if(a[i]==0) continue;
  if(f[ans]<a[i]) f[++ans]=a[i]; //如果满足上升那么改变子序列尾部元素值
  else   //如果不满足则需要更改前面的子序列的最小尾部值
  {
   int l=0,r=ans,mid;
   while(l<=r)  //查找这个新元素能放在哪个子序列尾部的后面,更改最小尾部值
   {
    mid=(l+r)/2;
    if(f[mid]>a[i]) r=mid-1;
    else l=mid+1;   
   }
   if(l!=0) f[l]=a[i];
  }
 }
 cout<<ans;
 return 0;
}

你可能感兴趣的:(算法)