回顾-二分

洛谷 1314 聪明的质监员
题意:数学公式,有n个产品 重量w 价值v,求m个区间里
在这里插入图片描述
(区间中 产品重量比W重的个数)*(区间中 产品重量比W重的产品价值的和)再将各个区间的yi加起来 就是Y。工厂的标准是s,找到一个W,使|Y-s|最小
分析:看到找到各种条件和与s最小,想到了二分,各个区间的和–前缀和。
前缀和
假设W,和所有产品使用yi,使用前缀和,找到各个位置W V方便求区间
[l,r]的和 == sum[r]-sum[l-1];(一定是l-1)

二分
我们很容易发现,W=0时,所有矿石都可以选上
当W大于最大重量时,一个都选不上
W值越大,选上的矿石越少,相反W值越小,选上的矿石越多
对应 W值越大,Y越小
所以 找到二分条件
Y>s 增大W来减小Y 减小|Y-s|
Y==s |Y-s|=0
Y<s 减小W来增大Y 减小|Y-s|

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<algorithm>
using namespace std;

typedef pair<int,int>Pair;
const int N = 2e5+10;

int n,m;
long long s;
int w[N],v[N];
long long ww[N],vv[N];
long long ss;
vector<Pair>ve;

bool check(int W)
{
    long long sum=0,l=ve.size();
    memset(ww,0,sizeof(ww));
    memset(vv,0,sizeof(vv));

    for(int i=1;i<=n;i++)
    {
        if(w[i]>=W)ww[i]=ww[i-1]+1,vv[i]=vv[i-1]+v[i];
        else ww[i]=ww[i-1],vv[i]=vv[i-1];
    }
    for(int i=0;i<l;i++)
    {
        sum+=(ww[ve[i].second]-ww[ve[i].first-1])*(vv[ve[i].second]-vv[ve[i].first-1]);
    }
    //cout<<W<<' '<<sum<<endl;
    ss=fabs(sum-s);
    if(sum>s)
        //cout<<W<<' '<<ss<<endl;
        return true;
    else
        return false;
}

int main()
{
    int l=2147483647,r=-1,mid;
    scanf("%d%d%lld",&n,&m,&s);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&w[i],&v[i]);
        r=max(r,w[i]);
        l=min(l,w[i]);
    }
    for(int i=0;i<m;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        ve.push_back(Pair{a,b});
    }
    long long ans=0x3f3f3f3f3f3f3f3f;
    while(l<r)
    {
        mid=(l+r+1)>>1;//不加+1 会死循环
        //cout<<i<<' '<<j<<' '<<mid<<endl;
        if(check(mid))l=mid;
        else r=mid-1;
        ans=min(ans,ss);
    }
    //printf("%d\n",l);
    printf("%lld\n",ans);

    return 0;
}

洛谷1281 书的复制
题意:有n种书,m个人,设计一种方案,一本书只能分给一个人,将n本书分配给m个人,前面尽量少分配,使得复制时间最短,复制时间为抄写页数最多的人用去的时间
分析:使用二分 判定性质:按照x 分配任务,从第一个任务开始分配,当sum>x 下一个人开始分配,分配完所有任务,需要的人s与m相比
我们容易发现 随着x的增大,需要的人s减少
所以
s>m 需要的人比m多 增大x 来减少s
s==m
s<m 需要的人比m少 减少x 来增大s
,找到最短最长的时间,按照时间去分任务,输出

#include<iostream>
#include<cstdio>
#include<string>
#include<cmath>
#include<vector>
using namespace std;

const int N = 600;
int n,m;
long long a[N];
int d[N][2],c=0;

bool check(int x)
{
    int s=1;
    long long sum=0;
    for(int i=n-1;i>=0;i--)
    {
        sum+=a[i];
        if(sum>x)
        {
            s++;
            sum=a[i];
        }
    }
    if(s>m)return false;
    else return true;
}

void print(int x)//输出写的有点麻烦
{
    int s=n-1,sum=0;
    for(int i=n-1;i>=0;i--)
    {
        sum+=a[i];
        if(sum>x)
        {
            //printf("%d %d\n",i+2,s+1);
            d[c][0]=i+2;
            d[c][1]=s+1;
            c++;
            sum=a[i];
            s=i;
            continue;
        }
        if(sum==x)
        {
            //printf("%d %d\n",i+1,s+1);
            sum=0;
            d[c][0]=i+1;
            d[c][1]=s+1;
            c++;
            s=i-1;
            continue;
        }
        if(i==0)
        {
            //printf("%d %d\n",i+1,s+1);
            d[c][0]=i+1;
            d[c][1]=s+1;
            c++;
        }
    }
    for(int i=c-1;i>=0;i--)
        printf("%d %d\n",d[i][0],d[i][1]);
}

int main()
{
    scanf("%d%d",&n,&m);
    long long sum=0;
    for(int i=0;i<n;i++)
        scanf("%lld",&a[i]),sum+=a[i];

    long long i=1,j=sum,mid;

    while(i<j)
    {
        mid=(i+j)>>1;
        //cout<<i<<'+'<<j<<' '<<mid<<endl;
        if(check(mid))j=mid;
        else i=mid+1;
    }
    print(i);
    //printf("%d\n",i);
    //cout<<s<<endl;

    return 0;
}

洛谷1083 借教室
题意:有n天 每天有教室r间 ,目前有m个订单,每个订单 d间教室从s借到t,找到第一个不满足的订单
分析:二分答案 前缀和求解
二分
一共有m个订单
每天需要的教室c 随着订单的增加增加
c>t[i] 减少订单数来减少c 找到第一个不满足的订单
c=t[i[ |c-t[i]|=0
c<t[i] 增大订单数来增大c 可以完成更多的订单

前缀和
使用差分 构造完成前x个订单 每天需要的教师数
[l,r需要d个教室 sum[l]+=d;sum[r+1]-=d;
i(1<= i <=n)天需要的教室 c+=sum[i];

ps:有一种情况,所有订单都可以完成 开始将r设置为m+1

#include<iostream>
#include<cstdio>
#include<cstring>

using namespace std;

const int N = 1e6+10;
int n,m;
long long t[N],tt[N];

struct Node{
    long long d,s,t;//借教室数目d 开始s 到t
}p[N];

bool check(int x)
{
    memset(tt,0,sizeof(tt));
    for(int i=1;i<=x;i++)//差分构造一下 [l,r]+r 到x订单 每天需要的教室数目
    {
        tt[p[i].s]+=p[i].d;
        tt[p[i].t+1]-=p[i].d;
    }

    int c=0;
    for(int i=1;i<=n;i++)//现有教室是否多于目前需要
    {
        c+=tt[i];
        //cout<<"i  "<<i<<"c   "<<c<<endl;
        if(c>t[i])return false;
    }
    return true;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%lld",&t[i]);
    for(int i=1;i<=m;i++)scanf("%lld%lld%lld",&p[i].d,&p[i].s,&p[i].t);

    int i=1,j=m+1,mid;//二分查找答案 将j=m+1 若都可以满足,则i最终等于m+1
    while(i<j)
    {

        mid=(i+j)>>1;
        //cout<<i<<'+'<<j<<' '<<mid<<endl;
        if(check(mid))i=mid+1;
        else j=mid;
    }
    if(i!=m+1)
        printf("-1\n%d\n",i);
    else printf("0");
    return 0;
}
/*
4 3
2 5 4 3
2 1 3
3 2 4
4 2 4
4 3
4 4 4 4
1 2 2
3 4 2
2 3 2
*/

POJ 3104 Drying

题意:有n件衣服,每件衣服含水量a[i],有一个暖气片每分钟可以使一件衣服水分减少k,衣服放在外面 每分钟水分减少1,怎么设计策略使用最少的时间烘干所有衣服(所有衣服的水分小于等于0)

分析:使用二分来求解
贪心的思想 一共有t分钟 每分钟所有衣服减1,所以最后所有衣服先减去t时间,然后判断剩下的衣服能不能使用t个(k-1)烘干完成(k中已经减去了1)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;

const int N = 1e5+10;
long long a[N],n,k;

bool check(long long x)
{
    long long s=x;
    for(int i=n-1;i>=0;i--)
    {
        if(a[i]>x)
            s-=(long long)ceil((double)(a[i]-x)/(k-1));
        if(s<0)return false;
    }
    return true;
}

int main()
{

    while(scanf("%lld",&n)!=EOF)
    {
        long long l=0,r=0,mid;
        for(int i=0;i<n;i++)scanf("%lld",&a[i]),r=max(r,a[i]);
        scanf("%lld", &k);

        while(l<r)
        {
            mid=(l+r)>>1;
            //cout<<l<<' '<<r<<' '<<mid<<endl;
            if(check(mid))r=mid;
            else l=mid+1;
        }
        printf("%lld\n", l);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值