二分法入门(二)——POJ 3258,2976;HDU 4430;CodeForces 535C;Gym 101194D;ACdream 1066

18 篇文章 0 订阅
3 篇文章 0 订阅

1.Bet ACdream - 1066


题意就是每次赌博有不同的可以买(买大买小这样的),每种有自己的赔率(赢了的回报为投入的钱y*回报率a[i]),每次赌博只能有一种是有回报的,要你求出最坏情况回报最大的值,策略就是把总金额y分成y[1],y[2],y[3]….使得y[1]*a[1]==y[2]*a[2]==……这样不论是哪一种获得回报你都会得到相同的回报,二分答案,然后判断钱能不能这样买。


#include<stdio.h>
#include<algorithm>
#include<iostream>
using namespace std;
double r,l,mid;
int n;
double money;
double c[105];
int main(){
    while(~scanf("%d",&n)){
        for(int i=0;i<n;i++){scanf("%lf",&c[i]);}
        scanf("%lf",&money);
        l=0,r=100000;
        while(r-l>0.00001){
            mid=(l+r)/2;
            double cnt=0;
            for(int i=0;i<n;i++){
                cnt+=mid/c[i];
            }
            if(cnt>money){r=mid;}
            else{l=mid;}
        }

        printf("%.2lf\n",mid);
    }

    return 0;
}

River Hopscotch POJ - 3258

最大化最小值,二分题里很经典的一种了,跳石子,删去一些石子使得跳跃距离的最小值最大,最后一块不能删(起点和终点)。

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<string>
#include<iostream>
using namespace std;
int l,n,m;
int lef,righ,mid;
int a[50050];
int main(){
    while(~scanf("%d%d%d",&l,&n,&m)){
        lef=0,righ=1000000001;
        for(int i=00;i<n;i++){
            scanf("%d",&a[i]);
        }
        a[n]=l;
        sort(a,a+n+1);
        while(righ-lef>1){
            int mid=(righ+lef)/2;
            int cnt=0,used=0;
            bool yes=1;
            for(int i=0;i<=n;i++){
                if(a[i]-cnt<mid){
                    if(i==n||used==m){yes=0;break;}
                    else{used++;}
                }
                else{cnt=a[i];}
            }
            if(yes){lef=mid;}
            else{righ=mid;}
        }
        printf("%d\n",lef);
    }

    return 0;
}

Dropping tests POJ - 2976

之前写过很类似的题,最大化平均值,通常策略就是二分答案再依据答案排序贪心选择,判断能不能满足mid。

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<string>
#include<iostream>
using namespace std;
int n,m;
double lef,righ,mid;
int a[1005],b[1005];
double c[1005];
bool cmp(double a,double b){return a>b;}

int main(){
    while(scanf("%d%d",&n,&m),m+n){
        m=n-m;
        lef=0,righ=100;
        for(int i=0;i<n;i++){
            scanf("%d",&a[i]);
        }
        for(int i=0;i<n;i++){
            scanf("%d",&b[i]);
        }
        for(int k=0;k<100;k++){
            mid=(righ+lef)/2;
            for(int i=0;i<n;i++){
                c[i]=(double)a[i]*1.0*100-(double)b[i]*1.0*mid;
            }
            sort(c,c+n,cmp);
            double cnt=0;
            for(int i=0;i<m;i++){
                cnt+=c[i];
            }
            if(cnt>=0){lef=mid;}
            else{righ=mid;}
        }
        printf("%.0lf\n",mid);
    }

    return 0;
}

Yukari’s Birthday HDU - 4430

以同心圆的方式给生日蛋糕插蜡烛,总共r圈,最内圈为b支蜡烛的情况下,第i圈插的蜡烛的数量为b^i,数量不能多也不能少,圆心可以插一根蜡烛也可以不插,求r*b的最小值。

因为蜡烛每一圈数量增长的速度非常快,所以r到50就足够满足数据范围了,枚举r然后寻找对应的b的最小值就行。

#include<iostream>
#include<string.h>
#include<stdio.h>
#include<string>
#include<vector>
#include<algorithm>
#include<queue>
using namespace std;
long long n;
long long r, lk, rk, midk;
int main(){
    while (cin >> n){
        n;
        long long ss = n - 1, a_r = 1, a_k = n - 1;
        for (int r = 2; r <= 50 && r <= n; r++){
            lk = 1; rk = 1000001;
            bool yes = 0;
            while (rk - lk > 1){
                midk = (rk + lk) / 2;
                long long cnt = 1;
                long long ans = 0;
                for (int i = 1; i <= r; i++){
                    ans += (cnt *= midk);
                    if (ans > n)break;
                }
                if (ans == n || ans == n - 1){
                    yes=1; rk = midk; break;
                }
                if (ans > n){ rk = midk; }
                else{ lk = midk; }
            }
            if (yes){
                if (r*rk < ss || (r*rk == ss&&r < a_r)){ ss = r*rk; a_r = r; a_k = rk; }
            }
        }
        cout << a_r << " " << a_k << endl;
    }
    return 0;
}

Tavas and Karafs CodeForces - 535C

#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
long long A, B, n;
long long l, t, m;
int main(){
    while (cin>>A>>B>>n){
        for (int k = 0; k < n; k++){
            cin >> l >> t >> m;
            if (t < (A + (l - 1)*B)){ printf("-1\n"); }
            else{
                long long beg = l, endd = (t - A) / B + 2;
                while (endd - beg>1){
                    long long mid = (beg + endd) / 2;
                    long long cnt = m*t;
                    long long sss = ((A + (l - 1)*B) + (A + (mid - 1)*B))*(mid - l + 1) / 2;
                    if (cnt >= sss){ beg = mid; }
                    else{ endd = mid; }
                }
                printf("%d\n", beg);
            }
        }
    }

    return 0;
}

Ice Cream Tower Gym - 101194D

构造冰淇凌塔需要下面一块是上面的两倍以上,给出n个冰淇凌球,要求塔高为k,问能构造出多少个冰淇凌塔,排序,二分可以构造出来的数量,然后贪心判断结果,我用的优先队列,先把前mid个球加入队列作为mid个塔的顶,然后顺序遍历数组,每次就与优先队列的top(塔高最小且最下面的球的面积最小)的比较能否满足,满足就pop队列顶然后push({s,h+1})进去,这是一种比较直观的做法,但是跑出来时间有点久,其实判断满足mid的时候也可以lower_bound当前球两杯大小来寻找,速度更快。

#include<iostream>
#include<string.h>
#include<stdio.h>
#include<string>
#include<algorithm>
#include<queue>
using namespace std;
int n, k;
long long b[300005];
struct ice{
    long long maxn;
    int h;
    bool operator <(const ice b)const{
        if (h == b.h){ return maxn>b.maxn; }
        return h > b.h;
    }
};
priority_queue<ice> que;

int main(){
    int t;
    scanf("%d", &t);
    for(int cas=1;cas<=t;cas++){
        //cin >> n >> k;
        scanf("%d%d", &n, &k);
        for (int i = 0; i < n; i++){
            cin >> b[i];
            //scanf("%lld", &b[i]);
        }
        sort(b, b + n);
        int beg = 0, endd = n / k+3;
        while (endd - beg>1){
            int mid = (endd + beg) / 2;//totalnum
            for (int i = 0; i < mid&&i<n; i++){
                que.push({ b[i], 1 });
            }
            for (int i = mid; i < n; i++){
                ice cnt = que.top();
                if (b[i] >= 2 * cnt.maxn){
                    que.pop();
                    que.push({ b[i], cnt.h + 1 });
                }
            }
            int total = 0;
            while (!que.empty()){
                if (que.top().h >= k)total++;
                que.pop();
            }
            if (total < mid){ endd = mid; }
            else{ beg = mid; }
        }
        cout << "Case #" << cas << ": " << beg << endl;
    }


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值