前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >挑战程序竞赛系列(16):3.1最大化最小值

挑战程序竞赛系列(16):3.1最大化最小值

作者头像
用户1147447
发布2019-05-26 09:21:37
3460
发布2019-05-26 09:21:37
举报
文章被收录于专栏:机器学习入门机器学习入门

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://cloud.tencent.com/developer/article/1434618

挑战程序竞赛系列(16):3.1最大化最小值

详细代码可以fork下Github上leetcode项目,不定期更新。

练习题如下:

花了两个月,刷完了初级篇,算入门了,心累。

POJ 3258: River Hopscotch

思路:属于二分,可以看作f(d)f(d)的一个函数,表示当前距离下,移除的石头,d越大,移除的石头也就越多。很明显是一种有序结构,所以直接在解空间中搜索满足条件的最小d即可。

代码语言:javascript
复制
static int[] rocks;
    static int N;
    public static void main(String[] args) throws IOException {
        Scanner in = new Scanner(System.in);
        int L = in.nextInt();
        N = in.nextInt();
        int M = in.nextInt();
        rocks = new int[N + 2];
        rocks[0] = 0;
        for (int i = 1; i <= N; ++i){
            rocks[i] = in.nextInt();
        }
        rocks[N+1] = L;
        Arrays.sort(rocks);
        lf = 0;
        rt = L;
        System.out.println(binarySearch(M));
    }

    static int lf;
    static int rt;
    public static int binarySearch(int M){
        while (lf < rt){
            int mid = lf + (rt - lf + 1) / 2;
            if (remove(mid) > M) rt = mid - 1;
            else lf = mid;
        }
        if (remove(lf) <= M) return lf;
        return 0;
    }

    public static int remove(int d){
        int cnt = 0;
        for (int i = 1, j = 0; i <= N + 1; ++i){
            if (rocks[i] - rocks[j] < d){
                cnt++;
            }else{
                j = i;
            }
        }
        return cnt;
    }

POJ 3273: Monthly Expense

思路:二分就不用说了,主要是f(m)f(m)函数,给定一个金钱范围,尽可能的塞满,问最多能塞出多少个桶。

代码如下:

代码语言:javascript
复制
static int N;
    static int M;
    static int[] money;

    public static void main(String[] args) throws IOException {
        Scanner in = new Scanner(System.in);
        N = in.nextInt();
        M = in.nextInt();
        money = new int[N];

        int max = 0;
        int sum = 0;
        for (int i = 0; i < N; ++i){
            money[i] = in.nextInt();
            max = Math.max(max, money[i]);
            sum += money[i];
        }

        int lf = max;
        int rt = sum;

        while (lf < rt){
            int mid = lf + (rt - lf) / 2;
            if (valid(mid) > M) lf = mid + 1;
            else rt = mid;
        }
        if (valid(lf) <= M) System.out.println(lf);
        else System.out.println(0);
    }

    public static int valid(int m){
        int cnt = 0;
        int sum = 0;
        for (int i = 0; i < N; ++i){
            sum += money[i];
            if (sum == m){
                sum = 0;
                cnt++;
            }
            else if (sum > m){
                sum = money[i];
                cnt++;
            }
        }
        if (sum != 0) cnt++;
        return cnt;
    }

POJ 3104: Drying

思路:

采用模拟,专注用最优策略,每次选择最湿的衣服用散热器,其余衣服减一,用优先队列保持当前最湿。

代码如下:

代码语言:javascript
复制
public static void main(String[] args) throws IOException {
        Scanner in = new Scanner(System.in);
        int N = in.nextInt();
        int[] clothes = new int[N];
        MaxPQ<Integer> pq = new MaxPQ<Integer>();
        for (int i = 0; i < N; ++i){
            clothes[i] = in.nextInt();
            pq.insert(clothes[i]);
        }
        int K = in.nextInt();
        int cnt = 0;
        while (!pq.isEmpty()){
            List<Integer> tmp = new ArrayList<Integer>();
            int c = pq.delMax();
            c -= K;
            if (c > 0) tmp.add(c);
            int size = pq.size();
            for (int i = 0; i < size; ++i){
                c = pq.delMax();
                c--;
                if (c > 0) tmp.add(c);
            }
            for (int t : tmp) pq.insert(t);
            cnt++;
        }
        System.out.println(cnt);
    }

TLE了,看来一定有数学性质来简化这过程。刚才的过程考虑了最优策略,而且程序的执行很navie,一步步走,或者你可以认为是单线程的(这也是为什么模拟慢的原因)。

所以优化的思路是能否利用一些数学性质来加快运算速度?脑子里蹦出来一个并发的概念,但必须符合并发才行啊,在哪里?此处只有一个地方有并发,每分钟自然风干时,所有的衣服都会自减一,思路就在这。而用散热器风干时,每个时刻只能操作一次,这意味着需要用散热器风干时,它就必须单独算一次。(计数是串行的)

所以优化思路是:减去所有衣服自然风干的状态(意味着模拟一下子被简化),计算剩余湿度需要散热器的次数。(而一件衣服的计数也可以在O(1)O(1)的操作内得到)

k⋅x+(mid−x)≥ai

k \cdot x + (mid - x) \ge a_i

得:

x≥⌈ai−midk−1⌉

x \ge \lceil \frac{a_i - mid}{k -1}\rceil

有趣的是,这道题如果不去假设某个mid存在,直接采用模拟很难在有限的时间内解决。而上述公式告诉我们一个事实,mid与x之间存在着映射关系,且符合有序,所以直接用二分搜索解空间就好。

代码如下:

代码语言:javascript
复制
static int[] clothes;
    static int K;
    static int N;
    public static void main(String[] args) throws IOException {
        Scanner in = new Scanner(System.in);
        N = in.nextInt();
        clothes = new int[N];
        int max = 0;
        for (int i = 0; i < N; ++i){
            clothes[i] = in.nextInt();
            max = Math.max(max, clothes[i]);
        }
        K = in.nextInt();
        if (K == 1){
            System.out.println(max);
            return;
        }
        int lf = 0;
        int rt = max;
        while (lf < rt){
            int mid = lf + (rt - lf) / 2;
            if (!valid(mid)) lf = mid + 1;
            else rt = mid;
        }
        System.out.println(lf);
    }

    public static boolean valid(int m){
        for (int i = 0, times = 0; i < N; ++i){
            int more = clothes[i] - m;
            if (more >= 0){
                times += (int) Math.ceil(more / (1.0 * (K - 1)));
                if (times > m) return false;
            }
        }
        return true;
    }

POJ 3045: Cow Acrobats

和二分没什么关系。。。可以直接求解,关键是需要证明一下。每次把力量和重量总和最大的放在最下面就好了。

证明:(反证法)

假设把力量和重量总和较小的牛放在最下面,那么它的risk一定比力量和重量综合最大的risk大。

所以,我们要确保的是,放入【力重总和】最大的那头牛后,剩余的可能risk都小于较小那头的risk。

还是画个图吧。

所以我们有理由选择最大的,而不是较小的,毕竟最大的能够保证risk较小,且经后的risk都小于等于risk2,接着我们采用归纳即可,最大的选择完毕,意味着剩余的元素是个子问题,继续选择次大的即可。(隐含着选择最大后,必须选择次大元素,而非其他元素的一个过程)

代码如下:

代码语言:javascript
复制
static class Cow implements Comparable<Cow>{
        int weight;
        int strength;
        public Cow(int weight, int strength){
            this.weight = weight;
            this.strength = strength;
        }

        @Override
        public int compareTo(Cow o) {
            return (o.weight + o.strength) - (this.weight + this.strength);
        }
    }

    static Cow[] cows;
    static int N;
    public static void main(String[] args) throws IOException {
        Scanner in = new Scanner(System.in);
        N = in.nextInt();
        cows = new Cow[N];
        long sum = 0;
        for(int i = 0; i < N; ++i){
            int w = in.nextInt();
            int s = in.nextInt();
            cows[i] = new Cow(w, s);
            sum += cows[i].weight;
        }
        Arrays.sort(cows);
        long risk = Integer.MIN_VALUE;
        for (int i = 0; i < N; ++i){
            sum -= cows[i].weight;
            risk = Math.max(risk, sum - cows[i].strength);
        }
        System.out.println(risk);
    }
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017年06月22日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 挑战程序竞赛系列(16):3.1最大化最小值
    • POJ 3258: River Hopscotch
      • POJ 3273: Monthly Expense
        • POJ 3104: Drying
          • POJ 3045: Cow Acrobats
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档