poj 1064 Cable master
二分
题意:给出n条线段,以米的单位给出,小数点后两位(精确到厘米),要你对这些线段裁剪,裁剪出m条等长的线段,并且让这些线段尽可能长另外线段的长度不能小于1厘米,如果筹不够m条,输出0.00
做法就是二分答案,但是二分的区间有讲究
一开始我认为长度的上限应该是 min { len[i] },即最短的那条线段,后来才想到是错的,应该是max{ len[i] },因为有一点想当然了,认为裁剪线段,就是每条线段都要裁剪,其实不是的,并不是每条线段都要裁剪
好像
4 2
2.00
3.00
4.00
答案是2.00吗?不是的,应该是3.00,虽然3.00大于第一条线段的长度,那么就不要裁剪第1条线段,直接从第2,第3条线段开始裁剪,就能裁剪出2条3.00长度的线段
另外,这题可以用浮点数的二分来写,但是基于题目的意思,完全可以转化为整数
因为最后长度不能小于1厘米,而一开始给出的线段也是精确到厘米的,而答案也是要求精确到厘米的,那么为什么我们不一开始就把所有的数据都改成用厘米来表示呢?然后直接用整数来二分就可以避免掉所有精度的问题。所以一开始的二分区间就是[1,max{len[i]}] , 不要从0开始,完全没有比较,因为答案最小要为1
最后一点,也是wa的地方,想了一阵子想明白了
我们二分答案,得到一个长度l,然后用所有线段去除l,看能裁剪出多少条,然后统计条数的总和count
然后就分类
count < m,说明这个长度l太长,筹不够m条,所以要缩点长度l,去左半区间二分
count = m,说明是一个合法的答案,记录,但是这样就可以了吗?可以跳出了吗?不是的,因为要找最大值,所以不能跳出,继续到右半区间二分
count > m , 这是不是一个合法的答案?一开始没想清楚,认为不是合法的答案,其实是的!另外要到右半区间继续二分找一个更大的值
下面的这个二分是错误的,就是因为count和m的判断出错
首先count>m的情况是一个合法的答案,最要命的是,可能找不到满足条件的count == m,这能找到count > m
例如
3 5
3.00
3.00
3.00
答案是1.00,当答案是1.00的时候count=9 > 5 , 当答案为2,3的时候,count都无法等于m,根本无法更新答案
因为想歪了一点,虽然题目要筹够m条,如果无法准确筹够m条的时间,只能缩短长度,筹出多余m条线段,因为裁剪多出来的线段,并不规定一定要用完
while(low <= high) { int mid = (low + high) >> 1; int count = 0; for(int i=0; i<n; i++) { int k = a[i] / mid; count += k; } if(count == m) { res = max(res , mid); low = mid + 1; } else if(count > m) low = mid + 1; else high = mid - 1; }
最后,用scanf读入数据,cin在G++会超时,在C++上没问题
用整数来二分
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> using namespace std; #define N 10010 #define MAX 10000000 int a[N]; int main() { int n,m; double len; while(scanf("%d%d",&n,&m)!=EOF) { int Max = 0; for(int i=0; i<n; i++) { scanf("%lf",&len); a[i] = len * 100; Max = max(Max , a[i]); } int low = 1 , high = Max; int res = 0; while(low <= high) { int mid = (low + high) >> 1; int count = 0; for(int i=0; i<n; i++) count += a[i] / mid; if(count >= m) res = max(res , mid) , low = mid + 1; else high = mid - 1; } printf("%.2f\n",(double)res / 100.0); } return 0; }
用浮点数二分,不推荐(没改scanf,交G++会超时)
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; const int N = 10010; const double E = 0.0001; const double M = 0.01; double a[N]; int main() { int n,m; double Max; while(cin >> n >> m) { Max = 0.0; for(int i=0; i<n; i++) cin >> a[i] , Max = max(Max,a[i]); double low = 0 , high = Max; while(high - low > E) { double mid = (low + high) / 2.0; int count = 0; for(int i=0; i<n; i++) { double s = a[i] / mid; int k = (int)s; count += k; } if(count >= m) low = mid; else high = mid; } int tmp = high * 100; double res = (double)tmp * 0.01; printf("%.2f\n",res); } return 0; }