zoukankan      html  css  js  c++  java
  • POJ2785 Values whose Sum is 0 二分

    希望自己以后能够及时做总结,不要拖,思路和逻辑真的很重要,其次是个人的代码实现能力,还有很长的路要走。

    这一题卡了两天,归根到底几种写法的思想是一样的,但也总算写通了。

    The SUM problem can be formulated as follows: given four lists A, B, C, D of integer values, compute how many quadruplet (a, b, c, d ) ∈ A x B x C x D are such that a + b + c + d = 0 . In the following, we assume that all lists have the same size n .

    Input

    The first line of the input file contains the size of the lists n (this value can be as large as 4000). We then have n lines containing four integer values (with absolute value as large as 2 28 ) that belong respectively to A, B, C and D .

    Output

    For each input file, your program has to write the number quadruplets whose sum is zero.

    Sample Input

    6
    -45 22 42 -16
    -41 -27 56 30
    -36 53 -37 77
    -36 30 -75 -46
    26 -38 -10 62
    -32 -54 -6 45
    

    Sample Output

    5
    

    Hint

    Sample Explanation: Indeed, the sum of the five following quadruplets is zero: (-45, -27, 42, 30), (26, 30, -10, -46), (-32, 22, 56, -46),(-32, 30, -75, 77), (-32, -54, 56, 30).

    解题思路:先分别求出前两列后两列的所有的和,
                      然后对后两列的和进行排序,
                      再对后两列求完和的每个元素在前两列的和进行二分查找看有                    多少种情况。

    需要注意一下二分的两个点:1.二分只能对于有序数组进行查找,所以需要先排序

                                                   2.二分模版只能找到所需要找到的特定元素,无法找到有多少个该元素,在这点上需要处理一下

    (原来二分不会超时。。。时间复杂度只有O(log2n)。)

    法一:

    直接用库函数过。

    (原来调用函数内存会是手动模拟的两倍多)

    //-45 22     -23   -72   -19  -14    48   -10             42 -16     26      -121
    //-41 -27    -68   8     12   -63    -1   -59           56 30      86      26
    //-36 53     17    -15   -11  -6     79   21            -37 77     40      39
    //-36 30     -6    -83   -79  -74    56   -2            -75 -46    -121     40
    //26 -38     -12   -99   -95  -90    -28  -70           -10 62     52       52
    //-32 -54    -86                                        -6 45     39       86
    
    
    //解题思路:先分别求出前两列后两列的所有的和,
    //然后对后两列的和进行排序,
    //再对后两列进行二分查找看有多少种情况
    
    #include<stdio.h>
    #include<algorithm>
    #include<string.h>
    using namespace std;
    #define inf 0x3f3f3f3f
    
    int a[5000],b[5000],c[5000],d[5000];
    int p[16000010],q[16000010];
    int n,k;
    
    int main()
    {
        while(~scanf("%d",&n))
        {
            memset(a,0,sizeof(a));
            memset(b,0,sizeof(b));
            memset(c,0,sizeof(c));
            memset(d,0,sizeof(d));
            memset(p,0,sizeof(p));
            memset(q,0,sizeof(q));
            int i,j;
            int ans=0;
            for(i=0; i<n; i++)
            {
                scanf("%d %d %d %d",&a[i],&b[i],&c[i],&d[i]);
            }
            k=0;
            for(i=0; i<n; i++) //看一下时间复杂度
            {
                for(j=0; j<n; j++)
                {
                    p[k]=a[i]+b[j];
                    q[k]=c[i]+d[j];
                    k++;
                }
            }
            sort(p,p+k);
            int sum=0;
            for(i=0;i<k;i++)
            {
                sum+=upper_bound(p,p+k,-q[i])-lower_bound(p,p+k,-q[i]);
            }
    
            printf("%d\n",sum);
        }
        return 0;
    }

    法二:这样写会超时

    #include<stdio.h>
    #include<algorithm>
    #include<string.h>
    using namespace std;
    #define inf 0x3f3f3f3f
    int a[5000],b[5000],c[5000],d[5000];
    int p[16000010],q[16000010];
    int n,k;
    int main()
    {
        while(~scanf("%d",&n))
        {
            memset(a,0,sizeof(a));
            memset(b,0,sizeof(b));
            memset(c,0,sizeof(c));
            memset(d,0,sizeof(d));
            int i,j,r;
            int ans=0;
            for(i=1; i<=n; i++)
            {
                scanf("%d %d %d %d",&a[i],&b[i],&c[i],&d[i]);
            }
            k=0;
            for(i=1; i<=n; i++) //看一下时间复杂度16000000 一亿多,一秒多,1.5s
            {
                for(j=1; j<=n; j++)
                {
                    p[k]=a[i]+b[j];
                    q[k]=c[i]+d[j];
                    k++;
                }
            }
            sort(p,p+k);
            int u,v;
            for(j=0; j<k; j++)
            {
                q[j]=-q[j];
                for(i=0; i<k; i++)
                {
                    if(p[i]>q[j])
                    {
                        v=i;
                        break;
                    }
                    else
                        continue;
                }
                if(i==k)
                    v=k;
                for(r=0; r<k; r++)
                {
                    if(p[r]>=q[j])
                    {
                        u=r;
                        break;
                    }
                    else
                        continue;
                }
                if(r==k)
                    u=k;
                ans+=v-u;
            }
            printf("%d\n",ans);
        }
        return 0;
    }

    法三:本来以为二分无法用,因为思维太局限了,认为二分只能找到特定的元素,无法计算出现的个数。但真的是人外有人,在这里借鉴了别人的思想,利用二分找到特定的元素之后,记录下标,再在该元素的前后寻找相同的元素计算个数即可。

    #include<stdio.h>
    #include<algorithm>
    #include<string.h>
    using namespace std;
    #define inf 0x3f3f3f3f
    int a[5000],b[5000],c[5000],d[5000];
    int p[16000010],q[16000010];
    long long ans;
    int n,k;
    void erfen(int x)//因为p定义的是全局变量,所以不用再传入了
    {
        int l=0,r=k-1;
        int mid;
        while(l<=r)
        {
            mid=(l+r)/2;
            if(x==p[mid])
            break;
            if(x>p[mid])
            {
               l=mid+1;
            }
            if(x<p[mid])
            {
                r=mid-1;
            }
        }
        if(x==p[mid])
        {
            int w=mid+1;//不能放在下面,因为mid经过下面的操作会改变
            while(p[mid]==x)
            {
                if(mid<0)
                    break;
                mid--;
                ans++;
            }
            while(p[w]==x)
            {
                if(w+1>k)
                    break;
                w++;
                ans++;
            }
        }//再找到特定元素之后,再在该元素的前后寻找相同的元素,这一点的处理值得学习
    }
    int main()
    {
        while(~scanf("%d",&n))
        {
            ans=0;
            int i,j;
            for(i=1; i<=n; i++)
            {
                scanf("%d %d %d %d",&a[i],&b[i],&c[i],&d[i]);
            }
            k=0;
            for(i=1; i<=n; i++) 
            {
                for(j=1; j<=n; j++)
                {
                    p[k]=a[i]+b[j];
                    q[k]=c[i]+d[j];
                    k++;
                }
            }
            sort(p,p+k);
             for(i=0; i<k; i++)
                erfen(-q[i]);
            printf("%lld\n",ans);
        }
        return 0;
    }

    法四:这个也算是手动模拟了第一个库函数的源代码了,内存比调用函数小两倍多。思路:利用二分找到两个下标,一个是大于等于需要查找的元素的下标,一个是大于的下标,两者之差就是出现的次数。需要注意的是:1.不能找大于和小于的,这个之前纠结了好久,因为如果不存在任何一种情况,会出现0-0的情况,假设一个0是大于的下标,小于的没有找到,那么也会默认为0,而下标为0所对应的元素是大于的,并不是我们所说的小于的。所以应该找大于等于和大于等于的,在集合中,这一部分是存在交集的,而大于和小于并不存在交集;2.考虑到找不到的情况,返回一个-1,不能返回为0,对于编译器会引起歧义,不知道是因为没有找到所以return 0还是返回的下标是0。返回-1之后还需要再进行判断,突然好佩服我自己哈哈哈哈。终于写出来了。

    //解题思路:先分别求出前两列后两列的所有的和,
    //然后对后两列的和进行排序,
    //再对后两列进行二分查找看有多少种情况
    
    #include<stdio.h>
    #include<algorithm>
    #include<string.h>
    using namespace std;
    #define inf 0x3f3f3f3f
    int a[5000],b[5000],c[5000],d[5000];
    int p[16000010],q[16000010];
    int ans;
    int n,k;
    int erfenz(int x)//因为p是全局变量,所以不用传入两个
    {
        int l=0,r=k-1;
        int mid;
        while(l<=r)
        {
            mid=(l+r)/2;
            if(x==p[mid])
                break;
            //     return p[mid];
            if(x>p[mid])
            {
                l=mid+1;
            }
            if(x<p[mid])
            {
                r=mid-1;
            }
        }//这道题是要求
        //一开始返回0,return0不能这样写,因为要是第一列全为0的话,返回也是0,不能确定是否找到;
        if(x==p[mid])
        {
            while(p[mid]==x)
            {
                if(mid<0)
                    break;
                mid--;
                ans++;
            }
            return mid+1;
        }
        //如果没有找到
        return -1;
    }
    
    int erfeny(int x)//因为p是全局变量,所以不用传入两个
    {
        int l=0,r=k-1;
        int mid;
        while(l<=r)
        {
            mid=(l+r)/2;
            if(x==p[mid])
                break;
            //     return p[mid];
            if(x>p[mid])
            {
                l=mid+1;
            }
            if(x<p[mid])
            {
                r=mid-1;
            }
        }
        //一开始返回0,return0不能这样写,因为要是第一列全为0的话,返回也是0,不能确定是否找到;
        if(x==p[mid])
        {
            int w=mid+1;
            while(p[w]==x)
            {
                if(w+1>k)
                    break;
                w++;
                ans++;
            }
            return w;
        }
        return -1;
    }
    
    
    int main()
    {
        while(~scanf("%d",&n))
        {
            ans=0;
            int i,j;
            for(i=1; i<=n; i++)
            {
                scanf("%d %d %d %d",&a[i],&b[i],&c[i],&d[i]);
            }
            k=0;
            for(i=1; i<=n; i++)
            {
                for(j=1; j<=n; j++)
                {
                    p[k]=a[i]+b[j];
                    q[k]=c[i]+d[j];
                    k++;
                }
            }
            sort(p,p+k);
    //        for(i=0; i<k; i++)
    //            erfen(-q[i]);
    
            int sum=0;
            int l,f;
            //for(j=0; j<k; j++)
            //{
                int v=0,u=0;
                for(i=0; i<k; i++)
                {
                    v=erfenz(-q[i]);
                    u=erfeny(-q[i]);
    
                    if(u==-1&&v==-1)
                    {
                       // u-v=0;
                       sum+=0;
    
                    }
    
                    if(u==-1&&v!=-1)
                    {
                       // u-v=0;
                       for(l=v+1;l<k;l++)
                       {
                           if(p[l]!=p[v])
                           {
                               f=l;
                           }
                       }
                  //     u-v=f-v;
                    int ff=f-v;
                    sum+=ff;// 或者直接用最后一个下标减去当前下标
    
                    }
    
                //    if(u!=-1&&v==-1)//不存在
                else
                    sum+=u-v;
                }
                
    //            if(i==k)
    //            {
    //                v=k;
    //            }
    //            for(i=0; i<k; i++)
    //            {
    //
    //            }
    //            if(i==k)
    //            {
    //                u=k;
    //            }
    
           // }
            printf("%d\n",sum);
        }
        return 0;
    }
  • 相关阅读:
    现代软件工程_团队项目_阿尔法阶段_后端python部分编程规范_2017.11.28
    现代软件工程_团队项目_阿尔法阶段_阿里云服务器部署_2017.11.24
    现代软件工程_团队项目_阿尔法阶段_前端知识分享_2017.11.22
    JavaScript基础(二)
    JavaScript基础(一)
    Socket接口
    【学习】CSS基础(一)
    【学习】HTML基础(二)
    【学习】HTML基础入门(一)
    现代软件工程_团队项目_阿尔法阶段_前端页面原型v1.0.2_2017.11.14
  • 原文地址:https://www.cnblogs.com/OFSHK/p/12650089.html
Copyright © 2011-2022 走看看