二分算法模板及oj练习题题解

用途

  • 实数二分
    在这里插入图片描述
    在这里插入图片描述

基础代码

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

模板

只需要while(l <= r){ l = mid + 1; r = mid - 1; 用一个ans记录可以的时候(can函数成立)的值即可 }

	int ans = 0;
	while (l <= r)
	{
		mid = (l + r) / 2;
		if (judge(mid))
		{
			r = mid - 1;
			ans = mid;
		}
			
		else
			l = mid + 1;
	}

题目

poj1064

n条线缆, 长度为L[i],从n条电缆中要切割出K条长度相同的电缆,最长可以是多长?

#include <cstdio>
#include <cmath>
using namespace std;
const int M=10005;
const double inf=200005.0;
double L[M];
int n,k;
bool judge(double x)
{
    int num=0;
    for(int i=0;i<n;i++)
      num+=(int)(L[i]/x);
    return num>=k;
}
void solve()
{
    double left=0,right=inf;
    for(int i=0;i<100;i++) //代替while(r>l) 避免了精度问题
    { //1次循环可以把区间缩小一半,100次可以达到10^(-30)的精度
        double mid=(left+right)/2;
        if(judge(mid)) left=mid;
        else right=mid;
    }
    printf("%.2f\n",floor(right*100)/100);
}
int main()
{
    while(scanf("%d%d",&n,&k)!=-1)
    {
        for(int i=0;i<n;i++)
          scanf("%lf",&L[i]);
        solve();
    }
    return 0;
}

poj2456

有m只牛,n间牛舍, 第i号牛舍在xi位置,要把每只牛放在离其他牛尽可能远的地方,即最大化两头牛的距离(最小值最大化问题)

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn= 999999;
int n;
int m;
int a[maxn];
bool can(int x)
{
	int cnt=1;
	int p=0;
	for(int i=1;i<n;i++)
	{
		if(a[i]-a[p]>=x)
		{
			cnt++;
			p=i;
		}
	}
	return cnt >= m;
}
int find()
{
	int l=0,r=a[n-1];
	int ans=0;
	while(l<=r)
	{
		//cout<<l<<" "<<r<<endl; 
		int mid=(l+r)/2;
		if(can(mid))
			{
				l=mid+1;
				ans=mid;
			}
		else r=mid-1; 
	}
	return ans;
}
int main()
{
//	cin>>n>>m;
//	for(int i=0;i<n;i++)
//		cin>>a[i];
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;i++)
		scanf("%d",&a[i]);

	sort(a,a+n);
//	cout<<find()<<endl;
	printf("%d",find());
	return 0;
}

poj1505

n本书,每本pi;m个人
每人分到连续若干本,且每本书只分给一个人。如果有多个解决方案,请输出第一个人工作量最小的解决方案
求使得每人分到的页数和的最大值 最小

  • 正确的代码:
#include<iostream>
#include<algorithm>
using namespace std;
#define N 1000
int *a;//保存页数
int n, m;// n 书的本数   m 人数
bool judge(int mid) // 假设 每人翻译mid页,判断是否可以完成
{
	int pagesum = 0;//记录当前一个人要翻译的页数
	int group = 1;//人数
	for (int i = n - 1; i >= 0; i--)
	{
		if (pagesum + a[i] > mid)//如果当前这个人页数再多一本书会超过mid
		{
			pagesum = a[i];//把这本新的书给下一个人
			group++;//人数++
		}
		else
			pagesum += a[i];			
	}
	if (group > m)//超过已有的人,这个假设 不能完成
		return false;
	else
		return true;
}
void output(int l)
{
	int group = 1;
	int pagesum = 0;
	bool *divider = new bool[n + 1];
	for (int i = 0; i <= n; i++)
		divider[i] = true;
	for (int i = n - 1; i >= 0; i--)
	{
		if (m - group == i + 1)
			break;
		if (pagesum + a[i] > l)
		{
			pagesum = a[i];
			group++;
		}
		else
		{
			divider[i] = false;
			pagesum += a[i];
		}
	}
	for (int i = 0; i < n /*- 1*/; i++)
	{
		cout << a[i] << " ";
		if (divider[i])
			cout << "/ ";
	} 
	cout<<endl;
}
int main()
{
	int t; cin >> t;
	while (t--)
	{
		cin >> n >> m;// n 书的本数   m 人数
		a = new int[n + 1];
		int l = 0, r = 0, mid = 0;
		for (int i = 0; i < n; i++)
		{
			cin >> a[i];
			r += a[i];
			if (l < a[i])
				l = a[i];
		}
		int ans = 0;
		while (l <= r)
		{
			mid = (l + r) / 2;
			if (judge(mid))
			{
				r = mid - 1;
				ans = mid;
			}
				
			else
				l = mid + 1;
		}
		//cout << "l=" << l << " ans=" << ans << endl;
		//output(l);//对
		output(ans);//对
	}
	::system("pause");
	return 0;
}

  • 不知道为什么无法运行:
#include<iostream>
#include <algorithm>
using namespace std;
const int maxn=999999;
int n,m;//n book m people
int a[maxn];
int T;
bool can(int x)
{
	int sum = 0;
	int people = 1;
	for(int i = n-1; i>=0; i++)
	{
		if(sum+a[i] > x)
		{
			sum=a[i];
			people++;
		}
		else
			sum+=a[i];
	}
	if(people>m)
		return false;
	else
		return true;
}
int find()
{
	int ans=0;
	int l=0,r=0;
	int mid = 0;
	for(int i=0; i<n; i++)
		r+=a[i];
	while(l<=r)
	{
		mid=(l+r)/2;
		if(can(mid))
		{
			ans = mid; 
			r=mid-1;
		}			
		else
			l=mid+1;
	}
	return ans;
}
void output(int x)
{
	int people = 1;
	int sum = 0;
	bool divider[maxn];
	for(int i  = 0; i <= n; i++)
		divider[i] = true;

	for (int i = n - 1; i >= 0; --i)
	{
		if(m - people == i + 1)//剩下的的人数和剩下的书的本数相等
			break;
		if(sum + a[i] > x)
		{
			sum = a[i];
			people++;
		}
		else//还是同一个人的书
		{
			divider[i] = false;//不拆开
			sum += a[i];
		}
	}
	for(int i = 0; i <= n; i++)
	{
		cout << a[i] << " ";
		if(divider[i])
			cout << "/ ";
	}
	cout<<endl;
}
int main()
{
	cin>>T;
	while(T--)
	{
		cin>>n>>m;
		for(int i=0;i<n;i++) 
			cin>>a[i];
		sort(a,a+n);
		output(find());
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值