• 二分专题(不排除非二分)


    Dropping tests

    In a certain course, you take n tests. If you get ai out of bi questions correct on test i, your cumulative average is defined to be

    .

    Given your test scores and a positive integer k, determine how high you can make your cumulative average if you are allowed to drop any k of your test scores.

    Suppose you take 3 tests with scores of 5/5, 0/1, and 2/6. Without dropping any tests, your cumulative average is . However, if you drop the third test, your cumulative average becomes .

    Input

    The input test file will contain multiple test cases, each containing exactly three lines. The first line contains two integers, 1 ≤ n ≤ 1000 and 0 ≤ k < n. The second line contains n integers indicating ai for all i. The third line contains n positive integers indicating bi for all i. It is guaranteed that 0 ≤ ai ≤ bi ≤ 1, 000, 000, 000. The end-of-file is marked by a test case with n = k = 0 and should not be processed.

    Output

    For each test case, write a single line with the highest cumulative average possible after dropping k of the given test scores. The average should be rounded to the nearest integer.

    Sample Input

    3 1
    5 0 2
    5 1 6
    4 2
    1 2 7 9
    5 6 7 9
    0 0

    Sample Output

    83
    100

    Hint

    To avoid ambiguities due to rounding errors, the judge tests have been constructed so that all answers are at least 0.001 away from a decision boundary (i.e., you can assume that the average is never 83.4997).

    分析:01分数规划,《挑战程序设计竞赛》上的最大化平均值;

    01分数规划链接:http://www.cnblogs.com/zzqsblog/p/5450361.html

    AC代码:

    #include<iostream>
    #include<cstdio>
    #include<string>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<stack>
    #include<vector>
    #include<set>
    #include<algorithm>
    #define N 1010
    #define INF 1000000000
    #define mem(a,b) memset(a,b,sizeof(a));
    typedef long long ll;
    using namespace std;
    ll a[N],b[N];
    double f[N];
    int n,k;
    bool cmp(double w,double v)
    {
        return w>v;
    }
    int Deal(double x)
    {
         for (int i=0;i<n;i++)
             f[i]=a[i]-x*b[i];
        double sum=0;
        sort(f,f+n,cmp);
        for (int i=0;i<n-k;i++)
           sum+=f[i];
        return sum>=0;
    }
    void erfen()
    {
         double l=0,r=2;
         while ((r-l)>1e-6)
        {
            double mid=(l+r)/2;
            if (Deal(mid))
               l=mid;
            else
                r=mid;
    }
       printf("%.0lf
    ",l*100);
    }
    int main()
    {
    
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    
    while (scanf("%d%d",&n,&k))
    {
        if (!n&&!k)
            break;
        for (int i=0;i<n;i++)
         scanf("%lld",&a[i]);
        for (int i=0;i<n;i++)
          scanf("%lld",&b[i]);
         erfen();
    }
    return 0;
    }
    View Code

     

    排列转换

    现在有两个长度为n的排列p和s。要求通过交换使得p变成s。交换 p[i]和 p[j]的代价是|i-j|。要求使用最少的代价让p变成s。

    Input单组测试数据。 
    第一行有一个整数n (1≤n≤200000),表示排列的长度。 
    第二行有n个范围是1到n的整数,表示排列p。每个整数只出现一次。 
    第三行有n个范围是1到n的整数,表示排列s。每个整数只出现一次。Output输出一个整数,表示从排列p变到s最少要多少代价。

    Sample Input

    样例输入1
    4
    4 2 1 3
    3 2 4 1

    Sample Output

    样例输出1
    3

     分析 :交换 p[i]和 p[j]的代价是|i-j|,所以我们求p[i]当前的位置和p[i]应该在的位置的绝对值,然后将它们相加,但是我们要注意最后的结果要除以2,因为一个数在左移的时候,与它交换位置的那个数同时在右移

    例如案例1:4在s中的3位置,ans+=2,ans=2; 2不用动;1在s中的4位置,ans+=1,ans=3;3在s中的1位置,ans+=3,ans=6;最后结果为ans/2;

    AC代码:

    #include<bits/stdc++.h>
    #define mem(a,b) memset(a,b,sizeof(a))
    #define N 200010
    typedef long long ll;
    using namespace std;
    ll a[N],b[N];
    ll f[N];
    int main()
    {
        ll n;
        cin>>n;
        for (ll i=1;i<=n;i++)
        {
            cin>>a[i];
            f[a[i]]=i;
        }
        ll ans=0;
        for (ll i=1;i<=n;i++)
        {
            cin>>b[i];
            ans+=abs(f[b[i]]-i);
        }
        cout << ans/2 << endl;
        return 0;
    }
    View Code

     

    Subsequence

    A sequence of N positive integers (10 < N < 100 000), each of them less than or equal 10000, and a positive integer S (S < 100 000 000) are given. Write a program to find the minimal length of the subsequence of consecutive elements of the sequence, the sum of which is greater than or equal to S.

    Input

    The first line is the number of test cases. For each test case the program has to read the numbers N and S, separated by an interval, from the first line. The numbers of the sequence are given in the second line of the test case, separated by intervals. The input will finish with the end of file.

    Output

    For each the case the program has to print the result on separate line of the output file.if no answer, print 0.

    Sample Input

    2
    10 15
    5 1 3 5 10 7 4 9 2 8
    5 11
    1 2 3 4 5

    Sample Output

    2
    3

    本题思想:题目要求选取最少连续的数字个数使得它们的和大于等于S

    ①时间复杂度O(n)=nlogn

    先求出前缀和sum,对于sum数组来说,如果满足条件sum[t]-sum[s]>=S则满足题目的条件,确定好起点s以后,我们用二分法求出sum中第一个大于等于sum[s]+S的数据,为什么不是S呢?如果是大于等于S的话那不就是相当于都是以第一个元素作为起点吗?对吧!!!

    ——|————————|—————————————|—————>

       sum[s] —S——  sum[t]                                    sum[n]

    我们从sum[s]到sum[n]中间查找大于等于S的区间S,即红色的区域,这就是为什么查找的条件大于等于sum[s]+S了!最后求出最小的差区间即可

    普通二分AC代码:

    #include<iostream>
    #include<cstdio>
    #include<string>
    #include<cstring>
    #include<queue>
    #include<set>
    #include<vector>
    #include<stack>
    #include<algorithm>
    #define N 100100
    #define mem(a,b) memset(a,b,sizeof(a))
    #define abs(x) return x>0?x:(-x);
    #define sqr(x) return x*x;
    #define INF 999999999
    typedef long long ll;
    using namespace std;
    
    ll a[N];
    int main()
    {
       ll n,m;
       int t;
       cin>>t;
       while (t--)
       {
          // ll num,mum;
           cin>>n>>m;
           ll sum=0;
           for (ll i=1;i<=n;i++)
               cin>>a[i];
           ll l=1,r=1;
           ll ans=INF;
           ll flag=0;
           while (1)
           {
               while (sum<m&&l<=n)
              {
                  sum+=a[l];
                  l++;
              }
              if (sum<m)
                  break;
              ans=min(ans,l-r);
              sum-=a[r++];
           }
            if (ans<=n)
                cout << ans << endl;
            else
                cout << "0" << endl;
    
       }
    }
     
    View Code

    下面讲一下复杂度为O(n)的算法:尺取法

    传说中的尺取法:反复地推进区间的开头和末尾,来求满足条件的最小区间的方法称为尺取法。

    ②我们先定起点为第一个数字,然后一直往后相加到和大于等于S,记录当前的最小区间,然后减去当前的第一个数字,如果数组里面的数字还没用完并且当前的和小于S的话,就继续往后相加,直到大于等于S,不断进行前面的这些操作,直到不满足条件退出,中间记录当前的最小区间,如果所有的数字加起来都本鞥大于等于S的话,就输出0。

    尺取法代码:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    typedef long long ll;
    using namespace std;
    #define N 100100
    ll a[N];
    int n;
    ll k;
    void solve()
    {
        ll sum=0,s=0,t=0;
        ll ans=n+1;
        while(1)
        {
            while (sum<k&&t<n)
            {
                sum+=a[t];
                t++;
            }
    
            if (sum<k)
               break;
                ans=min(ans,t-s);
            sum-=a[s++];
        }
        if (ans>n)
            ans=0;
        cout << ans << endl;
    }
    int main()
    {
        int t;
        cin>>t;
        while (t--)
        {
            cin>>n>>k;
            for (int i=0;i<n;i++)
                cin>>a[i];
               solve();
        }
        return 0;
    }
    View Code

     

     

    第K大的数

     数组A和数组B,里面都有n个整数。数组C共有n^2个整数,分别是A00 * B00,A00 * B11 ......A11* B00,A11 * B11......An1n−1 * Bn1n−1(数组A同数组B的组合)。求数组C中第K大的数。

     
    例如:A:1 2 3,B:2 3 4。A与B组合成的C包括2 3 4 4 6 8 6 9 12共9个数。

    Input第1行:2个数N和K,中间用空格分隔。N为数组的长度,K对应第K大的数。(2 <= N <= 50000,1 <= K <= 10^9) 
    第2 - N + 1行:每行2个数,分别是Aii和Bii。(1 <= Aii,Bii <= 10^9)Output输出第K大的数。Sample Input

    3 2
    1 2
    2 3
    3 4

    Sample Output

    9

     分析:题目有最多有50000个数字,暴力是肯定不能解决的吧!那就二分吧(刷的就是个二分专题)!那怎么二分呢?我一直找不到二分的方法,就觉得一定需要排序,排序好之后想是不是有简单的数学方法推出来呢!后来马上用一个例子推翻了自己的想法!后来看题解说是二分答案(其实这个专题有将近一半题目要用到二分答案,打算认真的归纳一下二分答案的解题),我们排序后,两个数组的最大值相乘就是整个和的最大值,最小值一样的,然后再最大值和最小值之间二分;二分的重点就是Check()函数,一定要向上取整,假设15/6的话,答案是2,但是比2大数字与6相乘不一定就会大于15,比如2*7;虽然AC了,但是我还是有个问题不能理解啊,比如输入一组数据

    3  2

    1 2 3

    2 3 5

    第二大的应该是10啊!但是代码运行的结果输出是9!这我就不是很能理解了!

    AC代码:

    #include<iostream>
    #include<cstdio>
    #include<string>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<queue>
    #include<set>
    #include<vector>
    #include<map>
    #define N 50050
    #define INF 1e+9
    typedef long long ll;
    using namespace std;
    //coding......
    ll a[N],b[N];
    int n,k;
    int C(ll mid)
    {
        ll num=0;
        for (int i=n-1;i>=0;i--)
        {
            ll ans=mid/a[i];
            if (mid%a[i])
                ans++;
           num+=n-(lower_bound(b,b+n,ans)-b);
        }
        return num>=k;
    }
    void solve()
    {
        ll lb=a[0]*b[0],ub=a[n-1]*b[n-1];
        while (ub-lb>1)
        {
            ll mid=(lb+ub)/2;
            if (C(mid))
                lb=mid;
            else
                ub=mid;
        }
        cout << lb << endl;
    }
    int main()
    {
        cin>>n>>k;
        for (int i=0;i<n;i++)
            cin>>a[i]>>b[i];
        sort(a,a+n);
        sort(b,b+n);
        solve();
        return 0;
    }
    View Code

     

     

     Face The Right Way

    Farmer John has arranged his N (1 ≤ N ≤ 5,000) cows in a row and many of them are facing forward, like good cows. Some of them are facing backward, though, and he needs them all to face forward to make his life perfect.

    Fortunately, FJ recently bought an automatic cow turning machine. Since he purchased the discount model, it must be irrevocably preset to turn K (1 ≤ K ≤ N) cows at once, and it can only turn cows that are all standing next to each other in line. Each time the machine is used, it reverses the facing direction of a contiguous group of K cows in the line (one cannot use it on fewer than K cows, e.g., at the either end of the line of cows). Each cow remains in the same *location* as before, but ends up facing the *opposite direction*. A cow that starts out facing forward will be turned backward by the machine and vice-versa.

    Because FJ must pick a single, never-changing value of K, please help him determine the minimum value of K that minimizes the number of operations required by the machine to make all the cows face forward. Also determine M, the minimum number of machine operations required to get all the cows facing forward using that value of K.

    Input
    Line 1: A single integer: N 
    Lines 2.. N+1: Line i+1 contains a single character, F or B, indicating whether cow i is facing forward or backward.
    Output
    Line 1: Two space-separated integers: K and M
    Sample Input
    7
    B
    B
    F
    B
    F
    B
    B
    Sample Output
    3 3
    Hint
    For K = 3, the machine must be operated three times: turn cows (1,2,3), (3,4,5), and finally (5,6,7)
     
    题意:给你一排牛的方向(F:正向    B:反向),问你用最少多少次以及一次最少让多少牛反转,题意是容易理解的,但是解题方法还是难想到;
    分析:这个题目同样是用尺取法,用暴力能的时间复杂度为n的三次方,一定超时!那么尺取法怎么个取法呢?我们先假设正向为0,反向为1;再循环K(从1到n),Check()函数中我们要注意,对下一个牛造成反转影响的只是它前面k-1个牛 ,所以我们需要减去前面k-1头牛对sum的影响

    AC代码:

    #include<iostream>
    #include<cstdio>
    #include<string>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define N 5010
    using namespace std;
    //coding.......
    char a[N];
    int dir[N];
    int visit[N];
    int n;
    int Check(int k)
    {
        memset(visit,0,sizeof(visit));
        int num=0,sum=0,i;
       for (i=0;i+k<=n;i++)
       {
           if ((dir[i]+sum)%2)
           {
               visit[i]=1;
               num++;
           }
           sum+=visit[i];
           if (i-k+1>=0)
             sum-=visit[i-k+1];
       }
    
       for (i=n-k+1;i<n;i++)
        {
             if((dir[i]+sum)%2)
               return -1;
             if (i-k+1>=0)
               sum-=visit[i-k+1];
        }
       return num;
    }
    void solve()
    {
        int M=n,K=1,m;
       for (int k=1;k<=n;k++)
       {
           m=Check(k);
           if (m>=0&&M>m)
           {
              M=m;
              K=k;
           }
       }
       cout << K << ' ' << M << endl;
    }
    int main()
    {
        cin>>n;
        for (int i=0;i<n;i++)
            {
                cin>>a[i];
                if (a[i]=='F')
                    dir[i]=0;
                else
                    dir[i]=1;
            }
        solve();
    }
    View Code
  • 相关阅读:
    table拖动列宽
    解决 wm_concat函数 长度不够问题
    「Luogu」[JSOI2007]字符加密 解题报告
    Markdown数学符号
    「P5004」专心OI
    「CF242E」XOR on Segment 解题报告
    「CF86D」Powerful array 解题报告
    「USACO08JAN」电话线Telephone Lines 解题报告
    「Luogu P2015」二叉苹果树 解题报告
    「Luogu P3866」[TJOI2009]战争游戏 解题报告
  • 原文地址:https://www.cnblogs.com/lisijie/p/7834062.html
Copyright © 2020-2023  润新知