二分暑假专题 训练记录 2017-7-29

POJ3258-River Hopscotch

题意:

  给你区间【0,L】给你n个石头,然后去除m个石头  最大化 石头间最小的距离

思路:

  首先0和L 这两个石头是不可以动的   然后用 s 数组记录 整个区间的石头

  然后排序  此时石头的排序就是有序的了  然后二分套模板

  接着check函数才是最关键的好的把

  从0到 n+1-m   总共就有 n+2-m 个石头了 而由于第0个石头不可以动 , 所以从第一个开始动 

  同时判断条件是 s[cur] - s[last] < d 而不是 <= 

 
#include <iostream>
#include <algorithm>

using namespace std;

int l,n,m;
int s[50010];

bool check(int d)
{
    int last = 0; // 1 - n+2-m
    for(int i=1;i < n+2-m ; i++)
    {
        int cur = last + 1;
        while (cur <= n+1 &&s[cur] - s[last] < d )
            cur++;
        if(cur > n+1) return 0;
        last = cur;
    }
    return true;
}

int main()
{
    cin>> l>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>s[i];
    sort(s,s+n+1);
    s[0] = 0,s[n+1] = l;

    int ans = 0;
    int le = 0,ri = l;
    while (le <= ri)
    {
        int mid = (le + ri)/2;
        if( check (mid) ) ans = mid , le = mid+1;
        else ri = mid-1;
    }
    cout << ans <<endl;
    return 0;
}
A

 

POJ 3104 Drying

题意:

  有n件衣服,每件衣服的含水量为ai单位,每分钟他们能自然脱水1单位,有一个脱水机,每次只能对一件衣服脱水,脱水量为k单位(脱水时不自然风干),问所有衣服全部风干的最小时间是多少?

 

思路:

  首先能够想到的是可以二分查找全部自然风干的最少时间mid。但这一题不同的是在判断函数中用蛮力法判断mid是否满足条件是会出错。需要特殊处理。

设某次二分出的一个值是mid
1、对于一件ai值小于等于mid的衣服,直接晾干即可;
2、对于一件ai值大于mid值的衣服,最少的用时是用机器一段时间,晾干一段时间,设这两段时间分别是x1和x2,那么有mid=x1+x2,ai<=k*x1+x2,解得x1>=(ai-mid)/(k-1) ,所以对(ai-mid)/(k-1)向上取整就是该件衣服的最少用时。

#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;
const int mod = 1e9 + 7;
const int maxn = 100000 + 5;
typedef long long LL;
int n,k,s[maxn];

bool check (LL d)//表示在d时间内能做玩
{
    LL sum = 0;
    for(LL i=1;i <= n;i++)
    {
        if(s[i] > d)
        {
            sum +=(s[i] - d + k-2 )/(k-1);//向上取整   +k-2 是为了向上取整
            if(sum > d) return 0;
        }
    }
    return true;
}
int main()
{
    while (~scanf("%d",&n))
    {
        for(int i=1;i<=n;i++)
            scanf("%d",&s[i]);
        scanf("%d",&k);
        sort(s+1,s+n+1);
        if(k == 1)//特判
        {
            cout<< s[n] <<endl;
            continue;
        }
        LL ans = 0;
        LL le = 0,ri = s[n];
        while (le <= ri)
        {
            LL mid = (le+ri)/2;
            if( check(mid) ) ans = mid,ri = mid-1;
            else le = mid +1;
            //cout<< mid <<endl;
        }
        cout<< ans <<endl;
    }
    return 0;
}
B_注意特判 k为1的情况

 

POJ 3045 Cow Acrobats(贪心)

题意:

  有N头牛玩叠罗汉游戏,每头牛有一个体重Wi和一个力量Si。 这个游戏对每头牛都有一个危险度等于这头牛上面的牛的体重总和减去他的力量值。

  求所有方案中危险值最大的最小。

思路: 

  按照w+s贪心叠,越大的越在下面。如果最优放置时,相邻两头牛属性分别为weightA,powerA,weightB,powerB,第一头牛在第二头上面,sum为第一头牛上面的牛的体重之和,那么

  第一头牛风险:a=sum - powerA;第二头牛风险:b=sum + weightA - powerB;

  交换两头牛位置之后a'=sum + weightB - powerA, b'=sum - powerB,

  所以此时  max(a,a') 与 max(b,b‘)比较

  由于是最优放置,所以w2-s1>=w1-s2,即w2+s2>=w1+s1,所以和最大的就该老实的在下面呆着= =!

  此题目有一个WA点 就是危险度可能是负数  所以刚开始初始化的 MAX 必须是比  -1e9 还要小的数.....WA了四次

//C_注意贪心规则
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string.h>
using namespace std;
const int mod = 1e9 + 7;
const int maxn = 50000 + 5;
const int INF = 0x3f3f3f3f;
typedef long long LL;

int n;
struct p{
    int weight,power;
    bool operator < (const p &x)const
    {
        //这里有问题的啊  应该是按照 weight+power 排序 而不是重量大力气小的排
        return weight + power > x.weight + x.power;

    }
}s[maxn];

LL sum = 0;
int main()
{
    while (~scanf ("%d",&n))
    {
        sum = 0;
        for(int i=1;i <= n;i++)
        {
            scanf("%d %d" , &s[i].weight , &s[i].power);
            sum += s[i].weight;
        }
        sort(s+1 , s+1+n);
        LL ans = -INF;
        for(int i=1; i<=n; i++)
        {
            sum =sum- s[i].weight;
            ans = max(ans,sum-s[i].power);
        }
        printf("%lld\n",ans);

    }
    return 0;
}
C_注意贪心规则

 

POJ1064 Cable master

题意:

  给你n,k   然后给出 n个a[i] 表示每次test 作对的数量   再给n个b[i] 表示每次test 的题数  让你去掉 k 次测验后  平均成绩的最大值

思路:

  看挑战 (最大化平均值)

  有句MMP 不知当讲不当讲

  最后结果的输出   错误 竟然就导致这道题 WA了6次;

  刚开始int (ans*100)    这个没有四舍五入

  改成 printf("%.0lf\n",ans*100); 然后网上找的数据   结果还是WA   我直接准备去 POJ 上交 然后蜜汁过了

  后来发现 VJ上我用的G++  

 

  最后网上搜索了一波 原来是 %f 和 %lf 的原因

  

double numG++提交C++提交最安全的方法
输入 scanf(“%lf”, &num); scanf(“%lf”, &num); cin >> num;
输出 printf(“%f”, num); printf(“%lf”, num); cout << num;
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int mod = 1e9 + 7;
const int maxn = 10000 + 5;
const int INF = 0x3f3f3f3f;

typedef long long LL;
int n,k;
int a[1010],b[1010];
double s[1010];

bool check(double d)
{
    for(int i=0; i<n; i++)
    {
        s[i] = double(1.000*a[i] - 1.000*d*b[i]);
    }
    sort(s,s+n);
    double sum = 0;
    for(int i=0; i<n-k; i++)
    {
        sum += s[n-i-1];
    }
    return sum >= 0;
}

int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    std::ios::sync_with_stdio(false);

    while (cin >> n >> k && n+k)
    {
        for(int i=0; i<n; i++)
            cin>>a[i];
        for(int i=0; i<n; i++)
            cin>>b[i];
        double le = 0,ri = 1;
        double ans = 0;
        for(int i=0; i<100; i++)
        {
            double mid = (le + ri)/2.0;
            if(check(mid))
                ans = mid, le = mid;
            else
                ri = mid;
        }
        printf("%.0lf\n",ans*100);
    }
    return 0;
}
D

 

poj 3685 Matrix

题意:

  Given a N × N matrix A, whose element in the i-th row and j-th column Aij is an number that equals i2 + 100000 × i + j2 - 100000 × j + i × j, you are to find the M-th smallest element in the matrix.   自己能读懂...

思路: 

  看别人的题解  发现 如果固定j  就发现函数是个递增函数  所以主函数里的二分是用来判断第M-th数的大小的

  然后check 函数里面的二分 是用来确定小于 m的个数的

  这个题挺好的  晚上再写一遍 加深对二分的理解

#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;

#define maxn 1e12;
typedef long long LL;
LL k ,n;
LL cal(LL i,LL j)
{
    return i*i + 100000 * i + j*j - 100000 * j + i*j;
}
bool check(LL m)
{
    LL cnt = 0;
    for(int j=1; j<=n; j++)//着相当于固定每一个j 求 <= m的个数
    {
        int le=1,ri = n,ans = 0;
        while (le <= ri)
        {
            int mid = (le + ri)/2;
            if(cal(mid,j) <= m)//如果此时小于等于m 就说明还可以找更大的i 所以左区间缩小
                ans = mid,le = mid+1;
            else
                ri = mid-1;
        }
        cnt += ans;
    }
    return cnt >= k;//如果cnt >= k ,此时就说明这不是第k小  所有让右区间缩小
}
int main ()
{
    std::ios::sync_with_stdio(false);
    int T;
    cin >> T;
    while (T--){
        cin >> n >> k;
        LL le = -100000*n;
        LL ri = n*n+n*n+100000*n+n*n;
        LL ans = 0;
        while (le <= ri)
        {
            LL mid = le+ (ri-le)/2;
            if( check (mid) )
                ans = mid,ri = mid-1;
            else
                le = mid+1;
        }
        cout<< ans <<endl;
    }
    return 0;
}
E 二分套二分

 

POJ3662 Telephone Lines

题意:

  拉电线:N个电线杆P条线可选,K条线内免费,否则花费免费额度外最长的那一根。求最小花费。

#include <iostream>
#include <queue>
#include <vector>
#include <functional>
using namespace std;
const int INF = 0x3f3f3f3f;
struct edge
{
    int to,cost;
    edge(int x,int y): to(x), cost(y){}
};
typedef pair <int,int> pii;


vector<edge> G[1010];
int d[1010]; //从起点到终点的距离
int V,E,K;

int dij(int s,int x)
{
    priority_queue<pii, vector<pii>, greater<pii> > q;
    for(int i=0;i <= V;i++ )
        d[i] = INF;
    d[s] = 0;
    q.push( pii(0,s) );//相当于把起点压进去  然后初始距离为0

    while( !q.empty() )
    {
        pii p =q.top(); q.pop();//取出来最短距离
        int v = p.second;
        if( d[v] < p.first ) continue;
        for(int i=0; i < G[v].size(); i++)
        {
            edge e = G[v][i];
            int dis = d[v] + (e.cost >= x ? 1 : 0);
            if( d[e.to] > dis)
            {
                d[e.to] = dis;
                q.push ( pii(d[e.to],e.to) );
            }
        }
    }
    return d[V-1];//到终点的最短距离
}

int main ()
{
    cin >> V >> E >> K;
    for(int i=0; i<E; i++)//无向图建立
    {
        int from,to,cost;
        cin >> from >> to >> cost;
        --from,--to;
        G[from].push_back( edge(to,cost) );
        G[to].push_back( edge(from,cost) );
    }
    int ans = 0;
    int le = 0,ri = 1000000+2;
    while (le <= ri)
    {
        int mid = (le + ri)/2;
        if(dij (0,mid) > K)//mid 取太小了
            ans = mid, le = mid+1;
        else
            ri = mid-1;
    }
    cout << (ans > 1000000? -1 : ans) <<endl;
    return 0;
}
E

 UVA1555-- Garland(推导+二分)

题意:

  有n个灯,给定第一盏灯A的高度,接下去每盏灯的高度按照公式计算,求使所有灯都不会落在地上(允许碰触)的B的最低高度。

思路:

  根据题意:H[i] = 2*H[i-1]+2 - H[i-2];

         不断确定H[2]的值 然后来逼进最小的H[n]

#include <iostream>
#include <cstdio>

using namespace std;
const int mod = 1e9 + 7;
const int maxn = 1000 + 5;
const int INF = 0x3f3f3f3f;
typedef long long LL;
int n;
double m,k;
double s[maxn];
bool check(double d)
{
    s[2] = d;
    for(int i=3;i <= n;i++)
    {
        s[i] = 2*s[i-1]+2 - s[i-2];
        if(s[i] < 0)
            return 0;
    }
    k = s[n];
    return 1;
}


int main()
{
    while (cin >> n >> m)
    {
        s[1] = m;
        double le = 0,ri = maxn;
        double ans = 0;
        for(int i=0;i < 100;i++)
        {
            double mid = (le + ri)/2.0;
            if(check(mid) )
                ans = mid,ri = mid;
            else
                le = mid;
        }
        printf("%.2lf\n",k);
    }

    return 0;
}
数学+二分而已

突然感觉二分怎么和数学关系还是蛮大的   毕竟给你一个函数,在某方面是单调性的话,就可以考虑用二分来降低复杂度

嗯...这几天写写二分  突然发现也不是想象中那么难,只是我学的不太精通罢了

posted @ 2017-07-28 23:05  Draymonder  阅读(325)  评论(0编辑  收藏  举报