poj1505Copying Books 二分+贪心详细总结

前两天花了时间理解了nyoj的586疯牛和nyoj619青蛙过河,满以为自己能重新写出这道题。。。谁知道。。。。。

题意:有m本书,k个人来抄,每本书有一个书本页数;求使得k个人抄完的最大页数最小,并且每个人都至少要抄一本,然后输出抄书的方案

分析

这里又涉及到前面nyoj的586疯牛和nyoj619青蛙过河说过的最大值中的最小值,  用前面的例子去理解比较方便

1.我们应该先用二分+贪心算出一个最大页数的最小值--这里和前面的类似

在二分的过程中,我们对于当前考虑的值 x  划分人数的贪心过程中,我们就有flag[i]去标记,这个位置应该划分开,

同时要注意的是:根据题意Output 最后一句If there is more than one solution, print the one that minimizes the work assigned to the first scriber, then to the second scriber
etc.  我们可以考虑每一次划分,我们从后面往前面划分:

例如:

5 3

100 100 100 100 100

output

100 / 100  100 / 100 100

而不是

100 100 / 100 100 / 100

2.然后就是根据这个最大的最小值去划分,并且,这里要保证每个人都有书抄,那么在二分+贪心过程中,可能出现得到的人数少于 k

例如:

5 4

100 100 100 100 100

二分的最大的最小值 为 200 那么就会划分成下面的样子

100 / 100 /
100 /100 100 红色加粗的那个划分就不会出现,因为是按照200 来划分的,那么此时,我们就应该从左往右开始添加划分,从左往右也是为了保证题意中要求前面的人抄的少的要求

上马:码上注释:

#include <queue>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

#define MAX 505
#define LL __int64

int M,k;
int book[MAX];
bool flag[MAX];//用来标记
int cnt;

int copy(LL x)
{
    cnt = 1;
    LL sum = 0;
    memset(flag,false,sizeof(flag));
    for(int i = M-1; i >= 0; i --)
    {
        sum += book[i];
        if(sum > x)
        {
            cnt ++;
            sum = book[i];
            flag[i] = true;
        }
    }
    return cnt;
}

void print()
{
    cout<<book[0];
    for(int i = 1; i < M; i ++)
    {
        if(flag[i-1]) cout<<" /";
        cout<<‘ ‘<<book[i];
    }
    cout<<endl;
}

int main()
{
    int T;
    LL l,r;
    cin>>T;
    while(T--)
    {
        cin>>M>>k;

        l = r = 0;
        for(int i = 0; i < M; i ++)
        {
            cin>>book[i];
            if(book[i] > l) l = book[i];
            r += book[i];
        }

        LL mid;
        while(l < r)
        {
            mid = (l+r)/2;
            if(copy(mid) <= k) r = mid;// - 1
            else l = mid + 1;
        }
        //得再分一次,不然  5 4
        //                  100 100 100 100 100
        //这个案例就会用上面的 l (不足200) 去切得到 100 / 100 / 100 / 100 / 100
        int cnt = copy(r);

        //对应于下面的案例<2> 可能分得不足k次,那么就把前面的每一本书分给一个人超(题目要求)
        for(int i = 0; i < M && cnt < k; i ++)
        {
            if(!flag[i]) flag[i] = true,cnt++;
        }
        print();
    }
    return 0;
}
/*
<1>
7 4
100 100 100 100 100 100 100
<2>
7 6
100 100 100 100 100 100 100

output
100 / 100 100 / 100 100 / 100 100
100 / 100 / 100 / 100 / 100 / 100 100
*/

个人愚昧观点,欢迎讨论与指正

poj1505Copying Books 二分+贪心详细总结,布布扣,bubuko.com

时间: 2024-12-26 08:36:57

poj1505Copying Books 二分+贪心详细总结的相关文章

uva 714 - Copying Books(贪心 最大值最小化 二分)

题目描述开头一大堆屁话,我还仔细看了半天..其实就最后2句管用.意思就是给出n本书然后要分成k份,每份总页数的最大值要最小.问你分配方案,如果最小值相同情况下有多种分配方案,输出前面份数小的,就像字典序输出从小到大一样的意思. 这里用到贪心的方法,定义f(x)为真的条件是满足x为最大值使n本书分成k份,那么就是求x的最小值.如何确定这个x就是用的二分法,x一定大于0小于所有值的合,不断的二分再判断是否成立,成立就取左半边,不成立说明太小了就取右半边,写的时候还是没有把二分法理解透彻,我还怕会丢失

二分+贪心

上海邀请赛热身时候,C题是一个二分+贪心的题目.起初并不会,问了旁边的复旦大神.这几天无意发现VJ上一个专题.擦原来是一个经典类型. 二分+贪心 这类题目注意数据范围,1e8,1e9一般都是这样. 注意事项 二分法有很多写法,推荐用lf+1 < rf的写法.这个也符合计算机中数据存取的原则.对于浮点数,直接就循环100次,精度绝对够. 一般有两种类型,一种是询问最优,即数列中无重复.一种是多个即lower_bound ,upper_bound这类函数问题. 贪心使用,就是这个问题枚举答案可被验证

nyoj586||poj2456 二分+贪心

完全看不懂题意....百度搜搜才看懂题意  然后就参考代码了 和yougth的最大化()nyoj914差不多的方法 二分+贪心 #include <stdio.h> #include <algorithm> using namespace std; int c,a[100005],n; bool judge(int k) { int p=a[0],cnt=1;//也就这里注意点 从1开始 自己想想为啥 for(int i=1;i<n;i++) { if(a[i]-p>=

HDU 4004 The Frog&#39;s Games 二分 贪心

戳这里:HDU 4004 //思路:二分经典入门题...贪心判方案是否可行 1 #include "bits/stdc++.h" 2 using namespace std; 3 int L, n, m; 4 int pos[500010], dis[500010]; 5 6 bool Cant(int Dis_Jump) 7 { 8 int i, Dis_Sum = 0, Count = 0; 9 for(i = 1; i <= n + 1; ++i) { 10 if(dis[

贪心(bnuoj49103+二分+贪心)

贪心 小明喜欢养小鸡,小鸡喜欢吃小米.小明很贪心,希望养s只不同种类的小鸡,小鸡也很贪心,每天除了吃固定的ai粒小米外,还想多吃bi*s粒小米. 小明每天有M(0<=M<=10^9)粒小米可以喂小鸡,小鸡共有N(0<=N<=1000)种.问小明最多可以养多少只小鸡? Input 多组数据,请读到文件尾 第一行,整数N,M,以空格分隔,之后两行,第一行为N个整数ai,第二行为N个整数bi. ai.bi都在int范围内 Output 一行一个整数,s. Sample Input 2 4

【bzoj2097】[Usaco2010 Dec]Exercise 奶牛健美操 二分+贪心

题目描述 Farmer John为了保持奶牛们的健康,让可怜的奶牛们不停在牧场之间 的小路上奔跑.这些奶牛的路径集合可以被表示成一个点集和一些连接 两个顶点的双向路,使得每对点之间恰好有一条简单路径.简单的说来, 这些点的布局就是一棵树,且每条边等长,都为1. 对于给定的一个奶牛路径集合,精明的奶牛们会计算出任意点对路径的最大值, 我们称之为这个路径集合的直径.如果直径太大,奶牛们就会拒绝锻炼. Farmer John把每个点标记为1..V (2 <= V <= 100,000).为了获得更加

poj1064 cable master(最大值问题:二分+贪心)

题意: 有n条电缆,他们的长度分别为l[i].如果从n条电缆中切割出K条长度相同的电缆的话,这k条电缆每条最长能多长?答案小数点后保留两位有效数字. 输入: n, k n行:l[i] Sample Input 4 11 8.02 7.43 4.57 5.39 Sample Output 2.00 数据范围: 1<=N<=10000; 1<=k<=10000; 1<=l[i]<=100000. 分析: 设命题:can(x)=能切割出k条长度为x的电缆. 问题转化:求can

HLG 1039 修路 (二分+贪心)

链接:http://acm.hrbust.edu.cn/index.php?m=ProblemSet&a=showProblem&problem_id=1039 Description 前段时间,某省发生干旱,B山区的居民缺乏生活用水,现在需要从A城市修一条通往B山区的路.假设有A城市通往B山区的路由m条连续的路段组成,现在将这m条路段承包给n个工程队(n ≤ m ≤ 300).为了修路的便利,每个工程队只能分配到连续的若干条路段(当然也可能只分配到一条路段或未分配到路段).假设每个工程队

sdut 2846 Remove Trees (二分 + 贪心)

题目 和poj 上的一道题几乎一样. 题意:已知n棵树距第一棵树的距离,求删掉m棵树后的 树之间 的最小距离  的最大值. 思路:二分枚举最小的距离,注意二分的写法. 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <queue> 5 #include <cmath> 6 #include <algorithm> 7 using nam