寒假笔记·二分法与滚动数组

二分法

当题目可能需要多个for循环叠加时,可以使用二分法来减少运算量。二分法的主要难点在编写check函数上。

例题:P1824 进击的奶牛

原题地址
题目描述

Farmer John建造了一个有N(2<=N<=100,000)个隔间的牛棚,这些隔间分布在一条直线上,坐标是x1,…,xN (0<=xi<=1,000,000,000)。

他的C(2<=C<=N)头牛不满于隔间的位置分布,它们为牛棚里其他的牛的存在而愤怒。为了防止牛之间的互相打斗,Farmer John想把这些牛安置在指定的隔间,所有牛中相邻两头的最近距离越大越好。那么,这个最大的最近距离是多少呢?

输入输出格式

输入格式:
第1行:两个用空格隔开的数字N和C。

第2~N+1行:每行一个整数,表示每个隔间的坐标。

输出格式:
输出只有一行,即相邻两头牛最大的最近距离。

输入输出样例

输入样例#1:
5 3
1
2
8
4
9

输出样例#1:
3
代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int qq[100010];
int r,l,n,m,mid;
int jug(int x)
{
    int i,le,cnt=1;
    le=qq[1];
    for(i=2;i<=n;i++)
    {
        if(qq[i]-le>=x)
        {
            cnt++;
            le=qq[i];
        }
        if(cnt>=m) break;
    }
    return cnt>=m;
}
int main()
{
    int i;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
        scanf("%d",&qq[i]);
    sort(qq+1,qq+n+1);
    l=1;
    r=qq[n]-qq[1];
    while(l<r-1)//二分法部分
    {
        mid=(l+r+1)/2;
        if(jug(mid)) l=mid;
        else r=mid;
    }
    if(jug(r)) printf("%d\n",r);
    else printf("%d\n",l);
    return 0;
}

例题:P1843 奶牛晒衣服

原题地址

题目描述

熊大妈请你帮助完成这个重任 。 洗完衣服后 , 你就要弄干衣服 。 衣服在

自然条件下用 1 的时间可以晒干 A 点湿度 。 抠门的熊大妈买了 1 台烘衣机 。

使用烘衣机可以让你用 1 的时间使 1 件衣服除了自然晒干 A 点湿度外,还

可以烘干 B 点湿度,但在 1 的时间内只能对 1 件衣服使用。

N 件衣服因为种种原因而不一样湿 , 现在告诉你每件衣服的湿度 , 要你

求出弄干所有衣服的最少时间(湿度为 0 为干 ) 。

输入输出格式

输入格式:
第一行 N , A , B ;接下来 N 行,每行一个数,表示衣服的湿度( 1 ≤ 湿度, A , B ≤ 500000 , 1 ≤ N ≤ 500000 ) 。

输出格式:
一行,弄干所有衣服的最少时间。

输入输出样例

输入样例#1:
3 2 1
1
2
3
输出样例#1:
1

代码:

#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int n,a,b;
int qq[500050];
bool check(int x)
{
    int k,y,i,j;
    k=0;
    for(i=1;i<=n;i++)
    {
        j=qq[i]-a*x;
        if(j<=0) continue;
        y=j/b;
        if(j%b) y++;
        k+=y;
        if(k>x) return 0;
    }
    if(k<=x) return 1;
    else return 0;
}

int main()
{
    int i,le,ri,mid,ans;
    scanf("%d%d%d",&n,&a,&b);
    for(i=1;i<=n;i++)
        scanf("%d",&qq[i]);
    sort(qq+1,qq+1+n);
    le=qq[1]/a;
    if(qq[1]%a) le++;
    ri=qq[n]/a;
    if(qq[n]%a) ri++;
    while(le+1<ri)//二分部分
    {
        mid=(le+ri+1)/2;
        if(check(mid)) ri=mid;
        else le=mid;
    }
    if(check(le)) ans=le;
    else ans=ri;
    printf("%d\n",ans);
    return 0;
}

一个超级超级巨大巨大的失误

例题:B. Alyona and a Narrow Fridge

原题地址

Discription
Alyona has recently bought a miniature fridge that can be represented as a matrix with h rows and 2 columns. Initially there is only one shelf at the bottom of the fridge, but Alyona can install arbitrary number of shelves inside the fridge between any two rows. A shelf is two cells wide, does not occupy any space but separates the inside of the fridge to the lower and upper part.

An example of a fridge with h=7 and two shelves. The shelves are shown in black. The picture corresponds to the first example.
Alyona has n bottles of milk that she wants to put in the fridge. The i-th bottle is ai cells tall and 1 cell wide. She can put a bottle on some shelf if the corresponding space above the shelf is at least as tall as the bottle. She can not put a bottle on top of another bottle (if there is no shelf between them). Two bottles can not share a cell.

Alyona is interested in the largest integer k such that she can put bottles 1, 2, …, k in the fridge at the same time. Find this largest k.

Input
The first line contains two integers n and h (1≤n≤10^ 3,1≤h≤10^9) — the number of bottles and the height of the fridge.

The second line contains n integers a1, a2, …, an (1≤ai≤h) — the heights of the bottles.

Output
Print the single integer k — the maximum integer such that Alyona can put the bottles 1, 2, …, k in the fridge at the same time. If Alyona can put all bottles in the fridge, print n. It is easy to see that Alyona can always put at least one bottle in the fridge.

Examples
input
5 7
2 3 5 4 1
output
3
input
10 10
9 1 1 1 1 1 1 1 1 1
outputy
4
input
5 10
3 1 4 2 4
output
5

代码:
此题为Codeforces Global Round 2的B题,在比赛中,笔者提交了如下代码,取得了通过:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<map>
#include<vector>
using namespace std;
int qq[1010],ww[1010],n,h;//qq[]存储牛奶高度值
int check(int x)
{
	int i,s=0;
	for(i=x;i>=1;i-=2)
	{
		s+=ww[i];
		if(s>h) return 0;
	}
	return 1;
}
int main()
{
	int i,j,le,mid,ri,ans;
	scanf("%d%d",&n,&h);
	for(i=1;i<=n;i++)
		scanf("%d",&qq[i]);
	ri=n;
	le=1;
	while(ri-1>le)//二分法部分
	{
		mid=(le+ri+1)/2;
		for(i=1;i<=mid;i++)
			ww[i]=qq[i];
		sort(ww+1,ww+mid+1);
		if(check(mid)) le=mid;
		else ri=mid;
	}
	if(check(ri)) ans=ri;//具体判断
	else ans=le;
	printf("%d\n",ans);
    return 0;
}

然而,在赛后的重判中,笔者的代码被以下数据hack:
Input
6 590
99 100 150 200 299 300
Output
6
Answer
5
主要问题是在结束二分部分后的具体判断过程前,未更新ww[]数组,导致具体判断check(ri)时,出现错误。
正确代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<map>
#include<vector>
using namespace std;
int qq[1010],ww[1010],n,h;
int check(int x)
{
	int i,s=0;
	for(i=x;i>=1;i-=2)
	{
		s+=ww[i];
		if(s>h) return 0;
	}
	return 1;
}
int main()
{
	int i,j,le,mid,ri,ans;
	scanf("%d%d",&n,&h);
	for(i=1;i<=n;i++)
		scanf("%d",&qq[i]);
	ri=n;
	le=1;
	while(ri-1>le)
	{
		mid=(le+ri+1)/2;
		for(i=1;i<=mid;i++)
			ww[i]=qq[i];
		sort(ww+1,ww+mid+1);
		if(check(mid)) le=mid;
		else ri=mid;
	}
	for(i=1;i<=ri;i++)
			ww[i]=qq[i];
	sort(ww+1,ww+ri+1);//重新更新ww[]数组
	if(check(ri)) ans=ri;//具体判断
	else ans=le;
	printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值