分治算法

分治算法

分治,字面上的解释是“分而治之”,就是把一个复杂的问题分成许多相似或者相同的子问题,再把子问题分成更小的问题,直到最后子问题可以直接简单求解,原问题的解即子问题的解的合并。

特征:

1.问题的规模缩小到一定规模就可以很容易的解决
2.问题可以分解成若干个规模较小的模式相同的子问题,即该问题具有最优子结构性质
3.合并问题分解出的子问题的解可以得到问题的解
4.问题所分解出的各个子问题之间是独立的,即子问题之间不存在公共的子问题

分治问题的实例:归并排序

在这里插入图片描述
代码如下:

void msort(int l,int r)
{
	if (l == r)return;
	int mid = (l + r) >> 1;
	msort(l, mid);
	msort(mid, r);
	int k = l;
	int i = l;
	int j = mid + 1;
	while (i <= mid && j <= r)
	{
		if (a[i] <= a[j])b[k++] = a[i++];
		else b[k++] = a[j++];
	}
	while (i <= mid)b[k++] = a[i++];
	while (j <= r)b[k++] = a[j++];
	for (int t = l; t <= r; t++)
	{
		a[t] = b[t];
	}
}

二分算法

概念:对于一个命题p(x=k),若存在k0使得k<k0时命题为真,当k>k0时命题为假,当k=k0时命题为真或假,则称命题p满足二分条件。二分算法能快速找到k0分界线。

一般步骤:

1.给出二分范围[l,r]
2.求范围中点mid=(l+r)/2
3.判断命题p(x=mid)的真假
4.若为真,则另l=mid,若为假,则另r=mid
5.循环2-4,直到精度满足要求,若精度为整数,则二分到r-l<=-1为止
模板如下

int binary_search(int l, int r)
{
	int mid;
	while (l + 1 > r)
	{
		mid = (l + r) >> 1;
		(check(mid) ? l : r) = mid;
	}
	return check(r) ? r : l;
}

举例:洛谷P2249在这里插入图片描述

#include<iostream>
using namespace std;
int a[1000001];
int bsearch(int l, int r, int k)
{
	int mid;
	while (l + 1 < r)
	{
		mid = (l + r) / 2;
		if (a[mid] < k)l = mid;
		else r = mid;
	}
	if (a[l] == k)return l;
	if (a[r] == k)return r;
	else return -1;
}
int main()
{
	int n, m;
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
	}
	int k;
	while (m--)
	{
		cin >> k;
		cout << bsearch(1, n, k) <<" ";
	}
	return 0;
}

二分答案

二分答案是二分最常见的形式
所谓二分答案,是指当答案满足二分单调性时,用二分将最优化问题转化为可行性问题
所谓最优化问题转化为可行性问题,就是指问题往往要求在某条件下求出某值的最大值或者最小值,这正是二分答案的边界
因此当答案满足二分单调性时,就可以在外面套一层二分答案,把求某值的最大值转化成一个猜测的答案,要求判断此答案是否满足要求
这就是二分答案的真正意义
举例洛谷P2678在这里插入图片描述
思路
题目中有个关键字:最短跳跃距离尽可能长(最大的最小,最小的最大,这是二分答案的敏感词)
二分跳跃距离,并且把这个距离认为时最短跳跃距离,然后以这个距离为标准移石头,使用一个check去判断这个解的可行性如果这个解可行,则去它的右边二分,如果不可行,就去他的左边二分
check怎么实现呢??
开始时你在i=0的位置我在跳下一步的时候判断我下一次要跳跃的距离,如果这个距离比二分出来的mid小,则需要拿走前面的石头(因为比最小跳跃距离还小的距离是不合法的),移走之后计数器加一。
模拟完这个过程后,我们查看计数器的值,这个值代表着我们需要移走石头的数量,然后判断这个数量是不是超了,超了就返回false,否则返回true

#include<iostream>
#include<algorithm>
using namespace std;
int a[50010];
int l;
int m;
int n;
bool check(int mid)
{
	int cnt=0;
	int i = 1;//第一块石头
	int j = 0;//起点
	while (i < n + 1)
	{
		if ((a[i] - a[j]) < mid)cnt++;//如果当前石头与下一次跳跃的石头的距离小于mid,则移走这块石头
		else j = i;
		i++;
	}
	if (cnt <= m)return true;
	else return false;
}
int bsearch(int l, int r)
{
	int mid;
	while (l + 1 < r)
	{
		mid = (l + r) / 2;
		if (check(mid))
		{
			l = mid;
		}
		else r = mid;
	}
	return check(l) ? l : r;
}
int main()
{
	cin >> l >> n >> m;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
	}
	
	a[n + 1] = l;
	if (n != 0 && m != 0) cout << bsearch(1, l);
	else cout << l;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青云遮夜雨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值