二分法---不光是查找值

挑战程序设计竞赛上这部分讲的很好~我自己做连poj 3273暴力都不知道怎么暴力orz

1、假定一个解并判断是否可行

poj 1064 cable master

给n根绳子,长度为Li,现在想把它们剪成k根相同的绳子,问最大每根绳子的长度是多少?

C(d)可以得到K条长度为x的绳子    floor(L_i/x)总和是否大于等于K

poj 3122 pie

给N个半径不同的派,分给f+1个馅饼,每个馅饼大小相同,并且要保证馅饼是从一块派上切下来的一块

C(d):可以得到f+1个馅饼 

//二分法查找求解单调函数
#include<iostream>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<stdio.h>
using namespace std;
const int N=1e4+10;
const double pi=acos(-1.0);
double s[N];
int n,f;
double bisort(int n)   //馅饼的个数
{
    double l=0;
    double r=s[n-1];    //每块馅饼面积上下界
    while((r-l)>1e-6)
    {
        int piece=0;
        double mid=(l+r)/2;
        //cout<<mid<<' ';
        for(int i=0;i<n;++i)
            piece+=s[i]/mid;
      //  cout<<piece<<endl;
        if(piece<f+1) r=mid;        //块数不够,要减少面积
        else if(piece>=f+1) l=mid;  //块数够,可以尝试增大面积
    }
    return l;
}
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        memset(s,0,sizeof(s));
        cin>>n>>f;
        for(int i=0;i<n;++i)
        {
            int r;
            cin>>r;
            s[i]=pi*r*r;
            //s[i]=r*r;
        }
        sort(s,s+n);
        /*for(int i=0;i<n;++i)
            cout<<s[i]<<endl;*/
        printf("%.4f\n",bisort(n));

    }
    return 0;
}

poj 1905 Expanding rods  简单的二分,只要找到目标的关系即可 第一次因为精度while(ub-lb>eps) 精度不够而wa

一根直木棍的两端被墙固定,受热的时候会弯曲改变长度,也会形成一个弧,问中心移动了多少。

设圆心角的一半为x,半径为a,则弧(木棒受热后长度)与弦(木棒原长)之间的比值为x/sinx,这在0-pi/2一个单调函数

\frac{2ax}{2asinx}=\frac{x}{sinx}

//A了
#include<stdio.h>
#include<math.h>
const double pi=acos(-1);
double l,n,c;
int main()
{
    while(scanf("%lf%lf%lf",&l,&n,&c)!=EOF)
    {
        if(l==-1&&n==-1&&c==-1)
            break;
        if(n==0) {printf("0.000\n");continue;}
        double coff=1+n*c;
        double lb=0,ub=pi/2;    //
        double mid;
        while(ub-lb>1e-15)  //一开始是1e-10wa了,刺激
        {
             mid=(lb+ub)/2;
            double cal=mid/sin(mid);  //在0-π/2单调递增函数
            if(cal>=coff) ub=mid;
            else lb=mid;
        }
        double x=l*(1-cos(mid))/(2*sin(mid));
        printf("%.3f\n",x);
    }
    return 0;
}

Gym 101174 Candle Box

注意点:我是先做3122再做1064的,两题很像,但是1064遇到了a不了的问题

(1)输出r而不是l

(2)输出用“lf”会错,要用“f”

(3)3122和1064是double型的,和int型的不一样,l=mid而不是mid+1

(4)很迷的一点是我本来想判断一下judge(ans)是否等于k,不是的话就直接输出0.00,但是删掉这句就A了,望赐教

//poj 1064
 int judge(double l)
{
    int cnt=0;
    for(int i=1;i<=n;++i)
        cnt+=(int)(len[i]/l);   //忘记置为整数了
    return cnt;
}
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;++i)
        scanf("%lf",&len[i]);
   double l=0,r=1e7;
   while((r-l)>1e-5)
   {
       double mid=(l+r)/2;
       if(judge(mid)>=k) l=mid;
       else r=mid;
   }
   double ans=floor(r*100)/100;
//if(judge(ans)!=k) printf("0.00\n")<<endl; else printf("%.2f\n",ans);
    printf("%.2f\n",ans);
   return 0;

}

2、最大化最小值

poj 2456 Agressive cows  用cin cout  T了

题意:N个牛棚C头牛,要求使任意两头牛之间的距离尽可能大

\small C(d):可以安排牛的位置使得任意牛的间距不小于\small x

#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
int N,C;
const int M=1e5+10;
int loc[M];
bool judge(int x)
{
    int cnt=1;
    int lo=loc[1];
    for(int i=2;i<=N;++i)
    {

        if(loc[i]>=lo+x)      //一开始写得loc[i]==lo+x
        {
            ++cnt;
            lo=loc[i];
        }
        if(cnt>=C) return true;
    }
    return false;
}
int main()
{
    scanf("%d%d",&N,&C);
    for(int i=1;i<=N;++i)
        scanf("%d",&loc[i]);
    sort(loc+1,loc+1+N);
    int l=0,r=loc[N]-loc[1];
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(judge(mid))
            l=mid+1;
        else
            r=mid-1;
    }
    printf("%d\n",r);
    return 0;
}

poj 3258 River Hopscotch

题意:河两岸相距L units, 河中有N块石头,给出他们到此岸的距离,John想移走其中M块,使得任意两块石头之间的距离尽可能大

转化:从此岸跳到彼岸要跳石头数+1次。

\small C(d):可以找到跳n+1-m次到达对岸且且最小距离不小于x,即每次距离都大于等于x

//poj 2456
#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
int N,C;
const int M=1e5+10;
int loc[M];
bool judge(int x)
{
    int cnt=1;
    int lo=loc[1];
    for(int i=2;i<=N;++i)
    {

        if(loc[i]>=lo+x)      //一开始写得loc[i]==lo+x
        {
            ++cnt;
            lo=loc[i];
        }
        if(cnt>=C) return true;
    }
    return false;
}
int main()
{
    scanf("%d%d",&N,&C);
    for(int i=1;i<=N;++i)
        scanf("%d",&loc[i]);
    sort(loc+1,loc+1+N);
    int l=0,r=loc[N]-loc[1];
    while(r-l>1)    //这种写法和double型的有很好的统一
    {
        int mid=(l+r)/2;
        if(judge(mid))
            l=mid;
        else
            r=mid;
    }
    printf("%d\n",l);
/*  A 还是更喜欢这种写法
while(l<=r)
    {
        int mid=(l+r)/2;
        if(judge(mid))
            l=mid+1;
        else
            r=mid-1;
    }
    printf("%d\n",l-1);   //输出r也是对哒
*/
    return 0;
}

3、最小化最大值

poj 3273 monthly expense 

题意:接下来N天农夫John每天要花money_i元,将这些天分为M季,每个季要花的钱尽可能少,每个季花的钱最少是多少

\small C(d):可以选择M个分组使得每个分组的和不大于x

刷了几道题之后陷入了疑惑,输出到底应该是l还是l-1还是r?

//连暴力我都不知道怎么做  A了
#include<stdio.h>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int a[N];
int n,m;
bool judge(int x)
{
    int sum=0;
    int cnt=0;
    for(int i=1;i<=n;++i)   //可以选择M个分组使得每个分组的和不大于x   最小化最大值
    {
        if(sum+a[i]<=x)
            sum+=a[i];
        else if(sum+a[i]>x)
        {
            sum=0;
            ++cnt;
            sum+=a[i];
        }
        if(cnt+1>m)  return false;  //最后一个分组没被加进去;分的组数过多,价格应该调大一点
    }
    return true;   //价格应该调小一点

}
int main()
{

    scanf("%d%d",&n,&m);
    int maxexp=0;
    for(int i=1;i<=n;++i)
    {
        scanf("%d",&a[i]);
        maxexp=max(maxexp,a[i]);
    }

    int l=maxexp,r=1e9+10;   //一开始上界开小wa了
    int mid;
    while(l<=r)
    {
         mid=(l+r)/2;
        if(judge(mid)) r=mid-1;
        else l=mid+1;
    }
    printf("%d\n",l);
    return 0;
}

 

4、最大化平均值

poj 2976 dropping tes   

题意:给出N个test,从自己的分数中删掉K个,使得比分最大 

审题不清楚导致输入错误,wa了,主要在于数据范围k>=0,可以为0

另外“.2f”可以自动四舍五入,如果不想四舍五入floor(l*100)/100  或者int(l*100)/100

\small C(d):可以选择n-k个测试使得单位比分不小于x   比分是否可以不小于x 

#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<iostream>
using namespace std;
const int N=1010;
int n,k;
int a[N],b[N];
double c[N];

bool judge(double x)  //可选的单位重量
{
    for(int i=1;i<=n;++i)
        c[i]=a[i]-x*b[i];
    sort(c+1,c+1+n);
    double sum=0;
    for(int i=n;i>k;--i)
        sum+=c[i];
    return sum>=0;

}
int main()
{
//   while(~scanf("%d%d",&n,&k))  //  WA  while(~scanf("%d%d",&n,&k)&&n&&k)
//    {
//        if(n==0&&k==0) break;
    while(~scanf("%d%d",&n,&k)&&(n||k))
    {

         for(int i=1;i<=n;++i)
            scanf("%d",&a[i]);
        for(int i=1;i<=n;++i)
            scanf("%d",&b[i]);
        double l=0,r=1.0;
        double mid;
        while(r-l>1e-10)
        {
             mid=(l+r)/2;
            if(judge(mid)) l=mid;
            else r=mid;
        }

//        int ans=floor(l*100);
//        printf("%d\n",ans);  会wa
        printf("%.0f\n",l*100);
    }

    return 0;

}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值