二分原理

作者:baihacker
来源:http://hi.baidu.com/feixue http://hi.csdn.net/baihacker

二分原理:
设f是定义在[a, b]上的bool函数,且满足性质若f(i) = true则f(i+1) = true.
那么算法:
int l = a, r = b;
while (l <= r)
{
 int mid = (l + r)/2;
 if (f(mid)) r = mid - 1;
 else l = mid + 1;
}
结束时一定有 l = r + 1;
那么有
l = b + 1或者l是最小的使得f的值为true的数.
同时
r = a - 1或者r是最大的使得f的值为false的数.
如果我们定义f(a-1) = false;f(b+1) = true;
那么可以描述为
l是最小的使得f的值为true的数.
r是最大的使得f的值为false的数.

在有序的序列x1,x2,x3,...,xn找到i,使得xi >= value
根据上面的原理:
a = 1, b = 1;
f(i) = xi >= value;

这就是stl中的lower_bound,类似地可以得到upper_bound.
总之,二分原理可以求出满足单调性的函数的第一个满足某条件的值.

应用1:
给定含有n个整数的序列,可以把这个序列分为m个子序列.
对于每个子序列,定义这个序列上的数的和为这个序列的权重.
所有子序列的权重中最大的称为这个划分的权重.
最小的划分权重是多大?

直接解决这个问题不好解决.
反过来思考,给定权重x,最少可以划为多少份,这个问题可以用贪心算法解决.
定义f(x) = 在权重x下最少的划分份数 <= m.
显然x太小的话,这个函数为假false,x够大的话,这个函数的值为true.
找到第一个最少划分份数 <= m的x,显然有:
如果取x-1,那么要至少分为大于m部分,不合要求.
如果对于x的最少划分刚好是m,显然,这是最小权.
如果对于x的最少划分小于m,留给读者自己思考.

可以看出,利用二分原理可以把复杂的问题,转换为判定性问题.

应用2:
|v0 - x| + |v1 - x| + ... + |vN-1 - x| <= M.
给定v0, v1,...,vN-1,和M,求使得这个等满足的x的个数.(都考虑整数)
解的可能的范围是
[max - M, min + M]
当vi取值大,M也大的时候,显然不能直接求解.

很显然,如果存在解,肯定是连续的一段,只需要找出这一段的最小值和最大值.
就可以知道有多少个解了.
利用二分原理,可以自然地想到在[max - M, min + M]上二分出最小,最大值.
以最小值为例f(x) = F(x) <= M,找到使f(x)为真的第一个数.(F为|v0 - x| + |v1 - x| + ... + |vN-1 - x|)
对应的最大值就是f(x) = F(x) <= M为真的最后一个数,注意二分原理,令g(x) = !f(x)
就是g(x)为假的最后一个数.(其实就是在二分过程中,把l,r交换一下,在结束以后取r而不是取l)
且慢,这里满足条件吗?
f(x)=true则f(x+1)=true吗?
显然不一定满足.

可以先求F的最小值点k,要么最小值点满足f(k)=true,要么解不存在.
这样就可以分别在[k-m, k], [k, k+m]上二分.

如何求最小值点,这个问题比较简单,留给读者自己思考.


应用3:
给定一定数量的钱,要配一台电脑,电脑由若干个部件组成.每个部件有不同的档次,
档次低的便宜但是得到的性能值小.又假定用统一的性能值衡量每个部件,性能值
最小值的部件为整体性能值.要求组装出性能值最大电脑.

应用4:
求出第k个素数.

扩展:
类似二分的,还有三分求凸(凹)函数极值.
有的二分写法是while (l < r)道理是一样的,结束的时候l = r,可以类似地给出二分原理,他们的本质是一样的.
直观地理解二分原理,要求第一个满足要求的值,就是在当前值满足要求的时候往更小的找,
否则就往更大的找.
要找出最后一个满足要求的,就是在当前值满足要求的时候往更大的找,否则往更小的找.

应用1:
http://cs.scu.edu.cn/soj/problem.action?id=3166
应用2:
http://cs.scu.edu.cn/soj/problem.action?id=3253
应用3:
http://cs.scu.edu.cn/soj/problem.action?id=3202
应用4:
http://topic.csdn.net/u/20091015/22/8e4f2bb8-0a95-40c7-917f-400703298968.html

 

买一送一:

矩阵二分

 


当一个序列满足
f(x) = a1 * f(x-1) + a2 * f(x-2) .. ak * f(x-k)
的时候,可以通过矩阵运算来解决求f(n)的问题

以k = 2, 为例,设f(1) = s1, f(2) = s2;
记向量F(x) = (f(x), f(x+1))'
矩阵A=
| a1 a2 |
| 1  0  |
我们来看看A*F(x)有什么结果

| a1 a2 |     |f(x+1)|    | a1*f(x+1) + a2*f(x) |
            *          =                 
| 1  0  |     |f(x)|      | f(x+1)               |

   | f(x+2) |
=             = F(x+1)
   | f(x+1) |
   
也就是A*F(x) = F(x+1)
那么 F(n) = A^(n-1) * F(1)
如果能直接求出A的方幂,再作用在F(1)上问题就解决了

回忆一下平方-和算法
int Power(int x, int n)
{
 int result = 1;
 for (; n; n >>= 1, x *= x) if (n&1) result *= x;
 return result;
}
显然可以用到矩阵上.

如何高效实现?
int data[3][10][10]; 
 
int (*m)[10] = data[0]; 
int (*r)[10] = data[1]; 
int (*t)[10] = data[2]; 
m,r初始化. 
for (; n; n >>= 1) 

   if (n&1) { t = m * r; swap(r, t);} //t = m * r要改成对应的矩阵乘以向量
   t = m * m; swap(m, t); //t = m * m要改成对应的矩阵乘以矩阵
}
这样的实现,可以避免矩阵拷贝,但是矩阵本身的乘法是避免不了的,在一定限度内是可忍受的.

一个小应用:
一个数字序列,要求长度为n,相邻元素的差异不超过k,求这个序列有多少种.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值