整数二分&浮点二分

A - Funky Numbers

 题意:给一个数num(max = 1e9),询问这个数可否由两个triangular number组成,triangular number:k(k + 1) / 2。

思路:最简单的思路就是打表,因为这个数虽然是1e9,但是可以从1开始打表到1e6(注意必须是1e6,因为1e5 * 9999 < 1e9),任意两个相邻的数的乘积,然后枚举第一个数,二分查找第二个数,最关键的是(two triangular numbers (not necessarily different !),那我也不用谈判是否重复了,直接使用二分函数binary_search()即可。

#include <iostream>
#include <cstring>
#include <algorithm>
const int N = 1e6;
typedef long long ll;
using namespace std;
ll a[N];
int main()
{  
    ios :: sync_with_stdio( false );
    cin.tie( NULL );
    for( ll i = 0; i < N; i++ ){
        a[i] = ( i + 1 ) * ( i + 2 ) / 2;
    }
    ll num;
    cin >> num;
    int f = 1;
    for( ll i = 0; i < N; i++ ){
        if( binary_search( a, a + N, num - a[i] ) ){
            cout << "YES";
            f = 0;
            break;//找到直接退出即可
        }
    }
    if( f ){
        cout << "NO";
    }
    return 0;
}

B - 银行贷款

 思路:写出公式就可以了,注意分母下面是n方。然后 L = 0, R = 1000,二分查找就可以了,注意的是如果check( mid ) >= x,说明月利率小了。还有这道题我用的是pow( ),其实最好是不要用这个函数,容易出现精度问题。这道题可以作为浮点数二分的板子。

#include <stdio.h>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
typedef long long ll;
using namespace std;
int x, y, n;
double check( double mid ){
    double sum = 0;
    for( int i = 1; i <= n; i++ ){
        sum += y / ( pow( 1 + mid / 100.0 , i ) );
    }
    return sum;
}
double b_search( ){
    double l = 0, r = 1000;
    int cnt = 100;
    while( cnt-- ){
        double mid = ( r + l ) / 2;
      //  cout << " l : " << l << " r : " << r << " mid : " << mid << endl;
        if( check( mid ) >= x )
            l = mid;//浮点数不要随意的加减1,要出问题直接这样就可
        else
            r = mid;
    }
    return l;
}
int main()
{  
    scanf("%d%d%d", &x, &y, &n );
    double ans = b_search();
    printf("%.1f", ans );//注意题意的精度问题
    return 0;
}

 C - River Hopscotch

题意:

这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 N 块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 M 块岩石(不能移走起点和终点的岩石)。

思路:这是最大最小问题,一般这种问题我们都是考虑二分求解,但也不全是,要具体情况具体分析。最大是通过b_search( )函数来实现,最小就是两两之间距离最小。这道题为了解题方便,把起点和终点的岩石也加入数组中。注意这道题没有说给出的岩石位置顺序就是从小到大的,所以需要sort排序,(而且有些题要注意方向,有些说的是与起点的距离,有些说的是与终点的距离。),而且这道题最小的 L == 原本的最短距离, R == 整个路的长度,然后进行二分。如何check成为关键点,我最开始是计算有多少个不满足的点,其实这样算是不对的,因为你一旦移开了一个石头,可能2个大于的点变成了一个,所以正确的解法是模拟人的真实进行过程,不断更新现在位置如果a[i] - nw < mid,就不满足,要满足就要搬石头,注意细想一下为什么是小于,而不是大于等于,如果满足这个mid就是针对这段距离的最小距离,就很好直接更新当前位置,直接跳过去。如果cnt小于等于m,说明这个长度很合适,而且cnt越小于m,len越合适,注意不一定要等于,因为小于了但满足条件,我可以多搬几个除了最小长度以外的石头。

#include <stdio.h>
#include <iostream>
#include <cstring>
#include <algorithm>
const int N = 5e4 + 10;
const int INF = 0x3f3f3f3f;
typedef long long ll;
using namespace std;
ll l, n, m, a[N], minn = INF;

bool check( ll mid ){
    ll cnt = 0;
    ll nw = 0;
    for( int i = 1; i < n + 2; i++ ){
        if( a[i] - nw < mid ){
            ++cnt;
        }
        else{
            nw = a[i];
        }
    }//妙啊
    return ( cnt <= m );
}

ll b_search( ){
    ll L = minn, R = l;//注意尽量剪少时间复杂度
    while( L < R ){
        ll mid = L + ( ( R - L ) >> 1 ) + 1;
        if( check( mid ) )
            L = mid;
        else
            R = mid - 1;
    }
    return L;
}
int main()
{  
    ios :: sync_with_stdio( false );
    cin.tie( NULL );
    cin >> l >> n >> m;
    a[0] = 0;
    for( int i = 1; i <= n; i++ ){
        cin >> a[i];
    }
    a[n + 1] = l;
    sort( a, a + n + 2 );
    for( int i = 1; i < n + 2; i++ ){
        minn = min( minn, a[i] - a[i - 1] );
    }
    cout << b_search();
    return 0;
}

D - Pie

题意:

我的生日快到了,并且我习惯上(traditionally)准备派。不仅仅是一个派,我有N个各种(various)味道各种大小的派。F个朋友要参加我的派对,并且每人都会得到一份派。他们每人都应该拿到一块派,并不是那种好几小片的看起来乱糟糟的。这应该是一整块派。如果其他人分到了较大的一块派,他们会生气并开始抱怨。所以他们都应该得到一块相同大小但不必是相同口味的派,虽然会导致一些派被糟蹋(spoil),也好过糟蹋派对。当然,我也想要一块派,并且这一块也得和其他人一样大。最大的问题是我们每人能得到多大一块派?所有派都是圆柱形的(cylindrical),并且它们都有相同的高——1,但是这些派的半径不同。也就是说不能合并pie,只能一个从一块中取。

思路:很明显这个是二分体积同时是个浮点数二分。L = 0, R  = 最大的半径,同时这道题为了方便,把pi都先不写,把体积用r^2表示,最后再乘上pi,check()函数就是看最后的数量与要求的数量的关系,如果大于了,说明分的半径小了。这道题很恶心的一点是居然卡我cin,cout,还必须用C++,#define pi acos( -1.0 )只能这样定义。(读懂题很重要)

#include <stdio.h>
#include <cstring>
#include <algorithm>
#include <cmath>
const int N = 1e4 + 10;
#define pi acos( -1.0 );
using namespace std;
int t, n, f;
double a[N], maxx;

bool check( double mid ){
    int cnt  = 0;
    for( int i = 0; i < n; i++ ){
        cnt += ( int )( a[i] / mid );
    }
    return ( cnt >= f );
}

double b_search( ){
    double l = 0, r = maxx;
    int cnt = 100;
    while( cnt-- ){
        double mid = ( l + r ) / 2;
        if( check( mid ) )
            l = mid;
        else
            r = mid;
    }
    return l;
}
int main()
{  
    scanf("%d", &t );
    while( t-- ){
        scanf("%d%d", &n, &f);
        maxx = -1;
        ++f;//细节,他自己还要吃蛋糕
        for( int i = 0; i < n; i++ ){
            scanf("%lf", &a[i] );
            a[i] *= a[i];//方便运算
            maxx = max( maxx, a[i] );//减少时间复杂度
        }
        double ans = b_search() * pi;
        printf("%.4lf\n", ans );
    }
    return 0;
}

 

F - K Best

 题意:有n个物品的重量和价值分别是w[i]和v[i],从中选出K个物品使得单位重量的价值最大。

思路:最开始这道题的思路就是贪心,把aver权值算出来,然后取最大的前k个,但是后面发现是不对的。如果按照价值/体积排序,贪心选取之后并不能保证最后的总平均价值最大。转化为不等式就是a+w1/b+v1>a+w2/b+v2,w1/v1>w2/v2 并不能保证上述不等式成立,每一种策略对式子的影响很大,不是简单的贪心,但是复杂的贪心我也不会啊~,走上正途,最大化均值问题用二分,而且这道题很明显不是整数二分,而是浮点数二分。啊啊啊!不想细写了,直接上代码~

#include <stdio.h>
#include <cstring>
#include <algorithm>
const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
using namespace std;
struct node{
    double aver;
    int loc;
}a[N];
int n, k, ans[N];
double v[N], w[N];

bool cmp( node a, node b ){
    return a.aver > b.aver;
}

bool check( double mid ){
    for( int i = 0; i < n; i++ ){
        a[i].aver = v[i] - mid * w[i];
        a[i].loc = i;
    }
    double sum = 0;
    sort( a, a + n, cmp );
    for( int i = 0; i < k; i++ ){
        sum += a[i].aver;
        ans[i] = a[i].loc;
    }
    return ( sum >= 0 );
}

void b_search( ){
    double l = 0, r = INF;
    int cnt = 50;
    while( cnt -- ){
        double mid = ( r + l ) / 2;
        if( check( mid) )
            l = mid;
        else
            r = mid;
    }
}

int main()
{  
    scanf("%d%d", &n, &k);
    for( int i = 0; i < n; i++ ){
        scanf("%lf%lf", &v[i], &w[i]);
    }
    b_search();
    int y = 1;
    for( int i = 0; i < k; i++ ){
        if( y == 1 ){
            printf("%d", ans[i] + 1 );
        }
        else{
            printf(" %d", ans[i] + 1 );
        }
        ++y;
    }
    return 0;
}

 

总结:

学习了整数二分和浮点二分,明白了两种整数二分之间的差别,以及二分的坑点。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
城市应急指挥系统是智慧城市建设的重要组成部分,旨在提高城市对突发事件的预防和处置能力。系统背景源于自然灾害和事故灾难频发,如汶川地震和日本大地震等,这些事件造成了巨大的人员伤亡和财产损失。随着城市化进程的加快,应急信息化建设面临信息资源分散、管理标准不统一等问题,需要通过统筹管理和技术创新来解决。 系统的设计思路是通过先进的技术手段,如物联网、射频识别、卫星定位等,构建一个具有强大信息感知和通信能力的网络和平台。这将促进不同部门和层次之间的信息共享、交流和整合,提高城市资源的利用效率,满足城市对各种信息的获取和使用需求。在“十二五”期间,应急信息化工作将依托这些技术,实现动态监控、风险管理、预警以及统一指挥调度。 应急指挥系统的建设目标是实现快速有效的应对各种突发事件,保障人民生命财产安全,减少社会危害和经济损失。系统将包括预测预警、模拟演练、辅助决策、态势分析等功能,以及应急值守、预案管理、GIS应用等基本应用。此外,还包括支撑平台的建设,如接警中心、视频会议、统一通信等基础设施。 系统的实施将涉及到应急网络建设、应急指挥、视频监控、卫星通信等多个方面。通过高度集成的系统,建立统一的信息接收和处理平台,实现多渠道接入和融合指挥调度。此外,还包括应急指挥中心基础平台建设、固定和移动应急指挥通信系统建设,以及应急队伍建设,确保能够迅速响应并有效处置各类突发事件。 项目的意义在于,它不仅是提升灾害监测预报水平和预警能力的重要科技支撑,也是实现预防和减轻重大灾害和事故损失的关键。通过实施城市应急指挥系统,可以加强社会管理和公共服务,构建和谐社会,为打造平安城市提供坚实的基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值