http://poj.org/problem?id=1064
给出n条线段,以米的单位给出,小数点后两位(精确到厘米),要你对这些线段裁剪,裁剪出m条等长的线段,并且让这些线段尽可能长另外线段的长度不能小于1厘米,如果筹不够m条,输出0.00。
很明显的用二分法求解题目,二分法的的试验值实际上为题目增加了一个条件,加上这个条件判断是否符合题目要求即可。
但需要注意答案的精度会对判断结束条件有要求。这个题答案要求小数点后两位数精度,那么二分法判断范围最高为0.01,实际上最好更低一些,0.0001更好。当然《挑战》书中给出的二分法循环100次能够让精度更高,也是不错的方法。
Source Code
Problem: 1064 User: liangrx06
Memory: 320K Time: 125MS
Language: C++ Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const int N = 10000;
const int INF = 0x3f3f3f3f;
int n, k;
double a[N];
bool check(double mid)
{
int cnt = 0;
for (int i = 0; i < n; i ++)
cnt += a[i]/mid;
return cnt >= k;
}
int main(void)
{
cin >> n >> k;
for (int i = 0; i < n; i ++)
scanf("%lf", &a[i]);
double lb = 0, ub = INF;
while (ub - lb > 0.001) {
double mid = (lb + ub) / 2;
if (check(mid)) lb = mid;
else ub = mid;
}
printf("%.2lf\n", floor(ub*100) * 0.01);
return 0;
}
http://poj.org/problem?id=2456
描述
农夫 John 建造了一座很长的畜栏,它包括N (2 <= N <= 100,000)个隔间,这些小隔间依次编号为x1,…,xN (0 <= xi <= 1,000,000,000).
但是,John的C (2 <= C <= N)头牛们并不喜欢这种布局,而且几头牛放在一个隔间里,他们就要发生争斗。为了不让牛互相伤害。John决定自己给牛分配隔间,使任意两头牛之间的最小距离尽可能的大,那么,这个最大的最小距离是什么呢?
输入
有多组测试数据,以EOF结束。
第一行:空格分隔的两个整数N和C
第二行——第N+1行:分别指出了xi的位置
输出
每组测试数据输出一个整数,满足题意的最大的最小值,注意换行。
样例输入
5 3
1
2
8
4
9
样例输出
3
这是一个最小值最大化的问题。先对隔间编号从小到大排序,则最大距离不会超过两端的两头牛之间的差值,最小值为0。所以我们可以通过二分枚举最小值来求。假设当前的最小值为x,如果判断出最小差值为x时可以放下C头牛,就先让x变大再判断;如果放不下,说明当前的x太大了,就先让x变小然后再进行判断。直到求出一个最大的x就是最终的答案。
Source Code
Problem: 2456 User: liangrx06
Memory: 640K Time: 125MS
Language: C++ Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 100000;
const int INF = 0x3fffffff;
int n, k;
int a[N];
bool check(int mid)
{
int x = a[0];
int cnt = 1;
for (int i = 1; i < n; i ++) {
if (x+mid <= a[i]) {
x = a[i];
cnt ++;
}
}
return cnt >= k;
}
int main(void)
{
cin >> n >> k;
for (int i = 0; i < n; i ++)
scanf("%d", &a[i]);
sort(a, a+n);
int lb = 0, ub = INF;
while (ub - lb > 1) {
int mid = (lb + ub) / 2;
if (check(mid)) lb = mid;
else ub = mid;
}
printf("%d\n", lb);
return 0;
}
http://poj.org/problem?id=3258
牛要到河对岸,在与河岸垂直的一条线上,河中有N块石头,给定河岸宽度L,以及每一块石头离牛所在河岸的距离,现在去掉M块石头,要求去掉M块石头后,剩下的石头之间以及石头与河岸的最小距离的最大值。
二分法求解,定义函数 check() 求距离x能否留下n-m个石子。循环结束条件是ub - lb > 1。
另外,此文对该题的思路做了更详尽的解析:
poj3258 二分及一些思考
Source Code
Problem: 3258 User: liangrx06
Memory: 444K Time: 157MS
Language: C++ Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 50000;
const int INF = 0x3fffffff;
int l, n, m;
int a[N];
bool check(int mid)
{
int x = 0;
int cnt = 0;
for (int i = 0; i < n; i ++) {
if (x+mid <= a[i]) {
x = a[i];
cnt ++;
}
}
if (x+mid <= l)
cnt ++;
return cnt >= n-m+1;
}
int main(void)
{
cin >> l >> n >> m;
for (int i = 0; i < n; i ++)
scanf("%d", &a[i]);
sort(a, a+n);
int lb = 0, ub = INF;
while (ub - lb > 1) {
int mid = (lb + ub) / 2;
if (check(mid)) lb = mid;
else ub = mid;
}
printf("%d\n", lb);
return 0;
}
http://poj.org/problem?id=3273
给N个数,划分为M个块(不得打乱数顺序)。找到一个最好的划分方式,使得块中的最大值 最小。
块的和最小大于最大数值,最大小于数值总和。因此,我们首先判断块的上限值(即我们要求的值),以该上限判断结果的划分是否符合要求。若划分的块数大于M,则我们二分式地减小这个上限;反之我们二分式地增加这个上限。
Source Code
Problem: 3273 User: liangrx06
Memory: 632K Time: 125MS
Language: C++ Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 100000;
const int INF = 0x3fffffff;
int n, m;
int a[N];
bool check(int mid)
{
int sum = 0;
int cnt = 1;
for (int i = 0; i < n; i ++) {
if (a[i] > mid)
return false;
if (sum + a[i] > mid) {
sum = a[i];
cnt ++;
}
else
sum += a[i];
}
return cnt <= m;
}
int main(void)
{
cin >> n >> m;
for (int i = 0; i < n; i ++)
scanf("%d", &a[i]);
int lb = 0, ub = INF;
while (ub - lb > 1) {
int mid = (lb + ub) / 2;
if (check(mid)) ub = mid;
else lb = mid;
}
printf("%d\n", ub);
return 0;
}
http://poj.org/problem?id=3104
有一些衣服,每件衣服有一定水量,有一个烘干机,每次可以烘一件衣服,每分钟可以烘掉k滴水。每件衣服没分钟可以自动蒸发掉一滴水,用烘干机烘衣服时不蒸发。问最少需要多少时间能烘干所有的衣服。
首先可以想到二分枚举答案。枚举一个mid值时,若一件衣服的水量大于mid,则一件衣服的最短时间是烘干一段时间,在自己蒸发一段时间。可以算出烘干的时间,为(a[i] - mid) / (k - 1)。这样把所有衣服的时间加起来,判断可行即可。
但此题可能还需要注意k=1的情况。(虽然测试数据似乎没有出现k=1的情况)
Source Code
Problem: 3104 User: liangrx06
Memory: 640K Time: 766MS
Language: C++ Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 100000;
const int INF = 0x3fffffff;
int n, k;
int a[N];
bool check(int mid)
{
int time = mid;
for (int i = 0; i < n; i ++) {
if (a[i] > mid)
time -= ceil((double)(a[i]-mid)/(k-1));
if (time < 0)
return false;
}
return time >= 0;
}
int main(void)
{
cin >> n;
for (int i = 0; i < n; i ++)
scanf("%d", &a[i]);
cin >> k;
int ans;
if (k == 1) {
ans = 0;
for (int i = 0; i < n; i ++)
ans = max(ans, a[i]);
}
else
{
int lb = 0, ub = INF;
while (ub - lb > 1) {
int mid = (lb + ub) / 2;
if (check(mid)) ub = mid;
else lb = mid;
}
ans = ub;
}
printf("%d\n", ans);
return 0;
}
http://poj.org/problem?id=3045
有N头牛要叠罗汉,每头牛都有相应的重量和力量。
叠罗汉是有危险的,每头牛的危险系数为该牛上面的牛的重量的和减去该牛的力量。
问如何安排这个叠罗汉顺序,使得危险系数最大的那头牛的危险系数最小。
最大值的最小值,用二分?二分当然也可以,但是有更简便的方法。
不妨设所有牛都按最优顺序排好了,考虑相邻的两头牛i和i+1,如果交换他们的位置,那么对前面和后面的结果都无影响,只是他们两个的风险值变化了(变大了),于是我们可以得到这个时候i和i+1的关系
设w1+w2+…+wi-1=W
那么如果i和i+1不交换:
i的风险值:W-si ①
i+1的风险值:W+wi-si+1 ②
如果i和i+1交换:
i+1(现在在第i个位置)的风险值:W-si+1 ③
i(现在在第i+1个位置)的风险值:W+wi+1-si ④
很容易可以看出②>③,④>①,而又假设原顺序是最优的,那么后来交换后肯定不是最优的,即②<④,即wi+si < wi+1+si+1,即最优序列一定满足wi+si < wi+1+si+1
所以算法很简单,按wi+si排序即可,这个题用贪心法。
总结:看见求最大的最小不能定式思维二分,当二分不行时可以观察题目的特点,抓住特点很重要。
Source Code
Problem: 3045 User: liangrx06
Memory: 640K Time: 110MS
Language: C++ Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 50000;
const int INF = 0x3fffffff;
struct Cow{
int w, s;
};
bool cmp(const Cow& a, const Cow& b)
{
return a.w+a.s < b.w+b.s;
}
int main(void)
{
int n;
Cow c[N];
cin >> n;
for (int i = 0; i < n; i ++)
scanf("%d%d", &c[i].w, &c[i].s);
sort(c, c+n, cmp);
int ans = -INF, sum = 0;
for (int i = 0; i < n; i ++) {
ans = max(ans, sum - c[i].s);
sum += c[i].w;
}
printf("%d\n", ans);
return 0;
}