zoukankan      html  css  js  c++  java
  • zoj 2968 Difference Game [贪心+二分]

    题目:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1967

    题意:多case,有两个集合Ga和Gb,两个集合最开始都有S个元素,可以将Ga中的元素移动到Gb中,也可以将Gb中的元素移至Ga中,每次移动的花费为移动前两集合中元素个数之差的绝对值,在保证花费不超过最大花费C的情况下,使Ga中最小的元素-Gb中最大的元素的值最大,化成式子就是求Max{min{Ga}-max{Gb}}。

    应该算是贪心吧,想了很久,推出了个结论过了,不过后面要用到二分,不过数据弱,不用二分也能过,正解应该要用二分查找的,不过写二分到是写苦了我,这次顺便总结一下二分。

    思路:

    1,只有最后min{Ga}>=max{Gb},最后输出的解才有可能为正数,而满足min{Ga}>=max{Gb}的情况就是将两个集合中的所有数重新分成两个集合,小的全部在Gb中,大的全部在Ga中。所以可以枚举所有情况,求出在满足不超过最大花费情况下的最大值即可,O(n);

    2,枚举所有正数的情况如何计算最小花费:从Ga,Gb的原始状况到最终状况出发,如果最后分给Ga的元素k,最开始也在Ga中,则为了保证花费最小,在转移过程中元素k也不可能会到Gb中去,所以可以计算出Ga到Gb的元素个数ab和Gb到Ga的元素个数ba,可以证明,最开始一定是,Ga->Gb,Gb->Ga,Ga->Gb……或者Gb->Ga,Ga->Gb,Gb->Ga……,如果令x=min{ab,ba},y=fabs(ab-ba),最后的花费就是2*x+y*(y-1)。就在求ab和ba的时候要用到二分:对于此时枚举的Ga中最小元素p,求出原Ga集合中有多少个比最小元素p小的数,排序后用二分找出下标即可,而ba与ab有这种关系ba = 2*n-i-(n-ab)

    3,如果最后没有正解,则说明Ga中的最小元素小于Gb中的最大元素。对于Ga,Gb原集合,第一步一定是将Ga中的最小元素移至Gb中 或 将Gb中的最大元素移至Ga中。假设将Ga中的最小元素移至Gb中,如果这样的第一步操作满足最大花费,由上述1,2可知无正解,则移动后Ga中次小元素仍然小于Gb中的最大元素,接下来可以移动Gb中的最大元素,或者原Ga集合中的次小元素(现在G啊a中的最小元素),但可证明一定是将Gb中的元素移至Ga中。

    如果是Ga->Gb,Gb->Ga,Ga->Gb花费为2;如果是Ga->Gb,Ga->Gb,Gb->Ga花费为6,而最后状态相同,所以不如选取前者,知道花费不满足条件就停止移动输出解。

    代码如下:

    View Code
     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<cmath>
     5 #include<algorithm>
     6 #define see(x) cout<<#x<<":"<<x<<endl;
     7 using namespace std;
     8 const int maxn = 20005;
     9 int a[maxn], b[maxn], c[maxn<<1];
    10 bool cmp(int p, int q){
    11     return p>q;
    12 }
    13 int search2(int temp, int n){
    14     int l=-1, r=n, m;
    15     while(l+1<r){
    16         m = (l+r)>>1;
    17         if(a[m]<temp){
    18             l = m;
    19         }
    20         else{
    21             r = m;
    22         }
    23     }
    24     return r;
    25 }
    26 int main(){
    27     int t, n, C, i, j, k, l, x, y, ans, res, cost;
    28     int ab, ba;
    29     scanf("%d",&t);
    30     while(t--){
    31         scanf("%d%d",&n,&C);
    32         for(i=0;i<n;i++){
    33             scanf("%d",&a[i]);
    34             c[i] = a[i];
    35         }
    36         for(i=0;i<n;i++){
    37             scanf("%d",&b[i]);
    38             c[i+n] = b[i];
    39         }
    40         if(n==1){
    41             printf("%d\n",a[0]-b[0]);continue;
    42         }
    43         sort(a,a+n);sort(b,b+n,cmp);
    44         sort(c,c+2*n);
    45         res = -50001;
    46         for(i=1;i<2*n;i++){
    47             ab = search2(c[i],n);
    48             ba = 2*n-i-(n-ab);
    49             x = min(ab,ba);
    50             y = (int)fabs(ab-ba);
    51             cost = 2*x+y*(y-1);
    52             if(cost<=C){
    53                 res = max(res,c[i]-c[i-1]);
    54             }
    55         }
    56         if(res>0){
    57             printf("%d\n",res);
    58         }
    59         else{
    60             cost = 0;
    61             i = j = 0;
    62             k = 0;
    63             while(cost<=C&&i<n&&j<n){
    64                 res = max(res,a[i]-b[j]);
    65                 cost += (int)fabs(i-j)*2;
    66                 if(k&1){
    67                     i++;
    68                 }
    69                 else{
    70                     j++;
    71                 }
    72                 k++;
    73             }
    74             cost = 0;
    75             i = j = 0;
    76             k = 0;
    77             while(cost<=C&&i<n&&j<n){
    78                 res = max(res,a[i]-b[j]);
    79                 cost += (int)fabs(i-j)*2;
    80                 if(k&1){
    81                     j++;
    82                 }
    83                 else{
    84                     i++;
    85                 }
    86                 k++;
    87             }
    88             printf("%d\n",res);
    89         }
    90     }
    91     return 0;
    92 }

    下面我要说说二分,以下是celia01写过的各种错误二分版本

    /*求a[]数组(其中元素是从小到大排序的)中值小于temp的元素有多少个,n为a[]数组大小*/
    //wa1
    int search2(int temp, int n){
        int l=0, r=n-1, m;
        while(l<r){
            m = (l+r)>>1;
            if(a[m]<temp){
                l = m+1;
            }
            else{
                r = m-1;//损失解
            }
        }
        return l;
    }
    //wa2
    int search2(int temp, int n){
        int l=0, r=n-1, m;  //r=n-1一开始就损失解了
        while(l<r){
            m = (l+r)>>1;
            if(a[m]<temp){
                l = m+1;
            }
            else{
                r = m;
            }
        }
        return l;
    }
    //wa3
    int search2(int temp, int n){
        int l=0, r=n-1, m;
        while(l+1<r){ //若l=3,r=4,a[3]<temp,进入死循环
            m = (l+r)>>1;
            if(a[m]<temp){
                l = m;
            }
            else{
                r = m;
            }
        }
        return r;
    }
    //accept:
    int search2(int temp, int n){
        int l=-1, r=n, m;
        while(l+1<r){
            m = (l+r)>>1;
            if(a[m]<temp){
                l = m;
            }
            else{
                r = m;
            }
        }
        return r;
    }

    首先说一下,对于二分while()里面常常又两种写法while(l+1<r)和while(l<r),一般

    while(l<r)  配  l=m+1 和 r=m-1

    while(l+1<r)  配  l=m 和 r=m

    这样才能有效跳出循环。题中要求a[]中小于temp的值有多少个,所以l=m+1无碍,但r=m-1就会损失解了。

    其实对于上面的wa2很容易改变,笔者今天又重新交了一下就ac了,居然一下排到了榜首,两个二分就都凑齐了

    //30ms
    int search2(int temp, int n){
        int l=0, r=n, m;
        while(l<r){
            m = (l+r)>>1;
            if(a[m]<temp){
                l = m+1;
            }
            else{
                r = m;
            }
        }
        return l; //这里l和r都无所谓
    }
    //40ms
    int search2(int temp, int n){
        int l=-1, r=n, m;
        while(l+1<r){
            m = (l+r)>>1;
            if(a[m]<temp){
                l = m;
            }
            else{
                r = m;
            }
        }
        return r; //这里一定要是r
    }
     
  • 相关阅读:
    Pandas索引和选择数据
    Pandas选项和自定义
    Pandas字符串和文本数据
    Pandas排序
    Pandas迭代
    Pandas重建索引
    Pandas函数应用
    Pandas描述性统计
    Pandas基本功能
    nyoj 234 吃土豆
  • 原文地址:https://www.cnblogs.com/celia01/p/2633598.html
Copyright © 2011-2022 走看看