• 2020年-05-26-习题训练三


    A - Sorted Adjacent Differences

     题意:

    给定一个序列,要求将它们排序使得i与i+1的差小于i+1与i+2小于i+2与i+3,依次排序。

    题解:

    先将它们从小到大排序,差最大值的肯定是最小的和最大的,然后差第二个就是最大的和次小的,第三个是次小的和次大的,依次输出就好了,注意下n为奇数的情况。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    int main(){
        int t,n;
        int g;
        cin>>t;
        while(t--){
            g=0;
            scanf("%d",&n);
            int a[n+10],b[n+10];
            for(int i=0;i<n;i++){
                scanf("%d",&a[i]);
            }
            sort(a,a+n);
            for(int i=0;i<n/2;i++){
                b[g]=a[n-1-i];
                g++;
                b[g]=a[i];
                g++;
            }
            if(n%2!=0){
                b[g]=a[n/2];
                g++;
            }
            for(int i=g-1;i>=0;i--){
                printf("%d ",b[i]);
            }
            cout<<endl;
        }
        return 0;
    }

    B - Powered Addition

    题意:

    给定一个序列,要求使它们变成后面一个数都比前面的数大于等于,再每一秒可以对它们任何数字加2^(s-1),s是秒数,问多少秒可以把它们变成这样的序列。

    题解:

    其实就是一个遍历,只要遇到后面的数比前面最大的数小的话,把它们标记下,找出一个数与最大的数差最多的值记为x,这样只有2^(s-1)>=x时,是最小的秒数。例如(1,7,6,5,就是在6的时候比7小所以起码要加到7,5的话也要加到7,它们之间差值最大为2,也就是2秒才可以)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    int main(){
        int t;
        cin>>t;
        while(t--){
            int n;
            scanf("%d",&n);
            int a[n+10];
            cin>>a[0];
            int maxn=a[0];
            int x=0;
            for(int i=1;i<n;i++){
                scanf("%d",&a[i]);
                maxn=max(maxn,a[i]);
                if(maxn>a[i]){
                    x=max(x,maxn-a[i]);
                }
            }
            //cout<<x<<endl;
            int ans=0;
            while(x>0){
                x=x-pow(2,ans);
                ans++;
            }
            printf("%d
    ",ans); 
        }
        return 0;
    } 

    C - Middle Class

     题意:

    给定n个数,它们之间可以加起来除以它们的个数,问最终有多少个数大于等于x

    题解:

    先把它们从大到小排序(如果最大的都比x小,那就是0)然后设一个sum为这些数加起来的和,如果平均数大于等于x的话就继续,否则就break,因为后面的数更小平均数更小。

    代码:

    #include<bits/stdc++.h>
    #define ll long long
    #define N 500001
    using namespace std;
    bool cmp(int p,int q){
        return p>q;
    }
    int main(){
        ll t;
        ll n,x,ans;
        double sum;
        int a[N];
        cin>>t;
        while(t--){
            memset(a,0,sizeof(a));
            sum=0;
            ans=0;
            cin>>n>>x;
            for(int i=0;i<n;i++){
                cin>>a[i];
            }
            sort(a,a+n,cmp);
        //    for(int i=0;i<n;i++){
        //        cout<<a[i]<<endl;
        //    }
            for(int i=0;i<n;i++){
                sum+=a[i];
                if(sum/(i+1)>=x){
                    ans++;
                }
                else{
                    break;
                }
            }
            cout<<ans<<endl;
        } 
        return 0;
    } 

    E - Kind Anton

     题意:

    给定一个a数组和b数组长度一样(a只有1 0 -1),可以把ai加到aj上(i<j),问能否使得把a变成b。

    题解:

    如果ai<bi只要判断i前面的a是否有1,有1的话就可以把ai变成bi了,如果ai>bi判断i前面的a是否有-1,有-1的话就可以把ai变成bi,如果都不满足这个条件就break。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    signed main()
    {
        int T;
        cin >> T;
        while(T--)
        {
            int n;
            cin>>n;
            int a[n+10],b[n+10];
            for(int i=0;i<n;i++) cin>>a[i];
            for(int i=0;i<n;i++) cin>>b[i];
            int flag1=0,flag2=0;
            int flag =0;
            for(int i=0;i<n;i++){
     
                if(a[i]<b[i]&&flag1==0)
                {
                    flag = 1;
                    break;
                }
                if(a[i]>b[i]&&flag2==0)
                {
                    flag =1;
                    break;
                }
                if(a[i]<0)
                    flag2 = 1;
                if(a[i]>0)
                    flag1= 1;
            }
            if(flag==1) cout<<"NO"<<endl;
            else cout<<"YES"<<endl;
     
        }
        return 0;
    }

    D - Circle of Monsters

    题意:

    有n个怪物围成一圈,每个怪物都有hp,消灭一个怪物会对下一个怪物造成x的伤害,你有一把武器,一个子弹一次只能打1hp,问如何使得你发射的子弹数量最少而消灭所有怪物。

    题解:

    根据例题:a1=7 b1=15 a2=2 b2=14 a3=5 b3=3 如果从a1开始打先需要打出a1发子弹,然后对a2造成了b1,因为b1>a2就不需要子弹,然后对a3造成了b2,也不需要子弹了,最终子弹就是a1=7.

    如果从a2开始打的话,对a2需要发射2发子弹,然后对a3造成了b2伤害可以消灭a3,就不需要子弹,再对a1造成了b3,因为b3<a1,所以a1怪物没有消灭,还需要补上a1-b3的子弹,最终子弹是6。

    从a3开始的话,最终子弹是9.

    根据例题可以得到 a1+(a2-b1)+(a3-b2)  a2+(a3-b2)+(a1-b3)  a3+(a1-b3)+(a2-b1) ,再把数据从环形变成直线,求前缀和(就是打死怪物需要的子弹)。(借鉴大佬题解)

    代码:

    #include<bits/stdc++.h>
    #define maxn 600010
    #define LL long long
    using namespace std;
    LL a[maxn],b[maxn],c[maxn],sum[maxn];
    int main(){
        int t,n,i;
        LL mn;
        scanf("%d",&t);
        while(t--){
            scanf("%d",&n);
            mn=1e18;//超出int范围的赋值
            for(i=1;i<=n;i++)scanf("%lld%lld",&a[i],&b[i]);
            for(i=n+1;i<=2*n;i++)a[i]=a[i-n],b[i]=b[i-n];//变环形为直线
            for(i=2;i<=2*n;i++)c[i]=((a[i]>b[i-1])?(a[i]-b[i-1]):0);//爆炸对应的子弹消耗
            //c是每一个怪物消耗的子弹
            //sum是到这个怪物消耗的子弹 
            sum[1]=0;
            for(i=2;i<=2*n;i++)sum[i]=sum[i-1]+c[i];//前缀和
            for(i=1;i<=n;i++)
                mn=min(mn,a[i]+sum[i+n-1]-sum[i]);
            printf("%lld
    ",mn);
        }
        return 0;
    }

    F - Eugene and an array

     题意:

    给定一个序列,如果它的子序列加起来不为0那就是好序列(比如41,-41,41,好序列就是41,-41,41另外两个不论怎样都是有41和-41存在为0就是坏的)。

    题解:

    其实也不是子序列而应该是从左开始减或者从右开始减的序列。 包含a[i]总共有n个左边i-1个,右边n-i个加上自己1个。然后枚举a[i]往左边可以枚举多少个好序列,再把值相加。

    那么很显然,左端点不可能在L3L3      L_3L3​的前面。那么是不是相当于我们在枚举a[i]的时候要维护,和为0的区间的最大左端点?是的。
    假设我们维护的这个最大左端点是max_l,那么对于每一个a[i],我们让ans += i-max_l就好了。
    维护就好了。可能难点在于怎么维护。我说说我的思路。我一看到区间和为0,我想到了前缀和,区间[i,j][i,j]      [i,j][i,j]和为0等价于sum[i−1]=sum[j]sum[i−1]=sum[j]      sum[i-1]=sum[j]sum[i−1]=sum[j]。那么我们可以先前缀和一下,然后考虑每一个a[i]的时候,直接找距离i最近的j且sum[j]=sum[i]。就能找到这个和为0的区间了,然后用这个更新max_l,再用max_l更新答案即可。

    (大佬思路借鉴)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn = 2e5 + 10;
    
    long long dp[maxn];
    
    map<long long, int> rec;//维护每个前缀和最近一次出现的位置
    void solve()
    {
        int n;
        cin >> n;
        for (int i = 1; i <= n; i++)
        {
            cin >> dp[i], dp[i] += dp[i - 1];
            rec[dp[i]] = -1;
        }
    
        long long ans = 0;
        int max_l = -1;
        rec[0] = 0;
        for (int i = 1; i <= n; i++)
        {
            if (rec[dp[i]] != -1) 
                max_l = max(max_l, rec[dp[i]]);
            rec[dp[i]] = i;
            ans += i - (max_l + 1);//max_l表示前缀和相等的下标,而max_l+1才是实际的区间
        }
        
        cout << ans;
    }
    
    int main()
    {
        solve();
        return 0;
    }
  • 相关阅读:
    6.11 修饰符的适用范围
    5.10 对象与垃圾回收
    5.9 枚举类
    5.8 java 11增强的Lambda表达式
    5.7 内部类
    5.6 接口
    5.5 抽象类
    5.4 final修饰符
    5.3 类成员
    5.2 Object类中两个需要被重写的方法
  • 原文地址:https://www.cnblogs.com/liyongqi/p/12988307.html
Copyright © 2020-2023  润新知