经典二分穷举
对二分的理解:
最后的答案一定在可确定的low与high之间,在题目中确定low和high即可!
然后由mid=(low+high)/2 来确认是否符合题意来确定最后的答案。
解决令最小。。。最大或最大。。。最小 等问题
POJ3273
题意:
将n个数分为m组,每组的天数必须是连续的,尽量让最大的一组数之和最小,最后输出最大的一组的和。
代码:
//Memory Time
//612K 297MS
#include<iostream>
using namespace std;
int n; //天数
int m; //规定的分组数
/*判断用当前的mid值能把天数n分成几组*/
/*通过比较group与m的大小,对mid值进行优化*/
bool judge_group(int mid,int money[])
{
int sum=0;
int group=1; //当前mid值能把n天分成的组数(初始把全部天数作为1组)
for(int i=1;i<=n;i++) //从第一天开始向下遍历每天的花费
if(sum+money[i]<=mid) //当前i天之和<=mid时,把他们归并到一组
sum+=money[i];
else //若 前i-1天之和 加上第i天的花费 大于mid
{
sum=money[i]; //则把前i-1天作为一组,第i天作为下一组的第一天
group++; //此时划分的组数+1
}
if(group>m)
return false; //若利用mid值划分的组数比规定的组数要多,则说明mid值偏小
else
return true; //否则mid值偏大
}
int main(void)
{
while(cin>>n>>m)
{
int* money=new int[n+1]; //每天花费的金额
int low=0; //下界
int high=0; //上界
for(int i=1;i<=n;i++)
{
cin>>money[i];
high+=money[i]; //把所有天数的总花费作为上界high(相当于把n天都分作1组)
if(low<money[i])
low=money[i]; //把n天中花费最多的那一天的花费作为下界low(相当于把n天分为n组)
} //那么要求的值必然在[low,high]范围内
int mid=(low+high)/2;
while(low<high) //可能在low==high之前,分组数就已经=m,但是mid并不是最优,因此要继续二分
{
if(!judge_group(mid,money))
low=mid+1; //mid值偏小,下界前移
else
high=mid-1; //mid值偏大,上界后移
mid=(low+high)/2;
}
cout<<mid<<endl; //二分搜索最后得到的mid值必然是使得分组符合要求的最优值,需要好好理解一下!!!
delete money;
}
return 0;
}
华丽的分割线================================================华丽的分割线
POJ3258
题意:
一条河长度为L,开始和终点分别有一块石头,中间有m块石头,可以移走n块,使得最小的石头间距最大
代码:
仍是二分查找问题,跟上一道题思路很像,也是分组问题,需要好好理解一下二分思想。
//Memory Time
//420K 391MS
#include<iostream>
#include<algorithm>
using namespace std;
int main(void)
{
int L; //河总长
int n; //河中石头数(除起点S和终点外E)
int m; //移除石头数
while(cin>>L>>n>>m)
{
/*Input & Initial*/
int* dist=new int[n+2]; //第i块石头到起点石头的距离为dist[i]
dist[0]=0; //起点S
dist[n+1]=L; //终点E
int low=L; //上界(一次跳跃的最短距离)
int high=L; //下界(一次跳跃的最大距离)
for(int i=1;i<=n+1;i++)
{
if(i<=n) //仅输入1~n,当i=n+1时仅用于寻找low
cin>>dist[i];
if(low > dist[i]-dist[i-1])
low=dist[i]-dist[i-1];
}
sort(dist,dist+(n+2)); //根据石头到S的距离升序排列
/*Binary-Search*/
while(low<=high)
{
int mid=(low+high)/2; //对最大跳和最小跳的距离折中,二分查找mid相对于最优解是偏大还是偏小
//假设mid是移除m个石头后的最短距离
int delrock=0; //利用当前的mid值能移除的石头数
int sum=0; //类比POJ 3273, 这里是 连续距离的累加值
//当在第i个距离累加后sum
for(int i=1;i<=n+1;)
{
if( (sum+=dist[i]-dist[i-1]) <= mid)
{
i++;
delrock++;
}
else //当从第i个距离累加到i+k个距离后,若sum>mid,则k个距离作为一段
{
i++;
sum=0; //sum置0,从第i+k+1个距离重新累加
}
}
if(delrock<=m) //本题难点之一:即使delrock==m也不一定找到了最优解
low=mid+1; //用当前mid值移除的石头数小于规定数,说明mid偏小
else
high=mid-1; //反之mid偏大
}
/*Output & Relax*/
cout<<low<<endl;
delete dist;
}
return 0;
}
华丽的分割线================================================华丽的分割线
POJ 3122
好开心!!!终于自己打出来一到二分题,虽然挺水的,不过也是有收获的!!
Pi 最好位数长一点,比如3.14159265359
还有控制精度大一点,比如保留三位就 1e-5 四位就1e-6
分pie问题
题意:
将n个pie分给f+1个人,每个人分到的pie必须只来自一个pie,并且每个人分到的pie是一样大的,保留三位小数
代码:
#include<cstdio>
#define pi 3.14159265359
#define eps 1e-5
double a[10050];
double low, high;
int n, f;
double mid;
bool judge(double mid)
{
int pie = 0;
for(int i = 0; i < n;i++)
{
if(a[i]/mid > 0)
{
pie+=a[i]/mid;
}
}
if(pie >= f + 1) //pie可以分给这些人,有可能比较小
return 1;
else return 0;//pie不够分,说明太大
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
scanf("%d%d", &n, &f);
double sum = 0;
for(int i = 0; i < n; i++)
{
int tmp;
scanf("%d", &tmp);
a[i] = tmp * tmp * pi;
sum += a[i];
}
low = 0;
high = sum / (f + 1);
while(high - low > eps)
{
mid = (low + high) / 2;
if(judge(mid))
{
low = mid;
}
else
{
high = mid;
}
}
printf("%.4f\n", mid);
}
return 0;
}
华丽的分割线================================================华丽的分割线
POJ1905
题意:
不重要,自己再搜,主要学到一个公式,角度→弧度公式 θr = s(θ为度数,r为半径,s为度数对应的弧长),主要是这道题还要倒方程!
代码:
//Memory Time
//244K 0MS
#include<iostream>
#include<math.h>
#include<iomanip>
using namespace std;
const double esp=1e-5; //最低精度限制
int main(void)
{
double L,n,c,s; //L:杆长 ,n:温度改变度 , c:热力系数 ,s:延展后的杆长(弧长)
double h; //延展后的杆中心 到 延展前杆中心的距离
double r; //s所在圆的半径
while(cin>>L>>n>>c)
{
if(L<0 && n<0 && c<0)
break;
double low=0.0; //下界
double high=0.5*L; // 0 <= h < 1/2L (1/2L并不是h的最小上界,这里做一个范围扩展是为了方便处理数据)
double mid;
s=(1+n*c)*L;
while(high-low>esp) //由于都是double,不能用low<high,否则会陷入死循环
{ //必须限制low与high的精度差
mid=(low+high)/2;
r=(4*mid*mid+L*L)/(8*mid);
if( 2*r*asin(L/(2*r)) < s ) //h偏小
low=mid;
else //h偏大
high=mid;
}
h=mid;
cout<<fixed<<setprecision(3)<<h<<endl;
}
return 0;
}
华丽的分割线================================================华丽的分割线