算法训练第一周题解汇总

A - Sort the Subarray

大意:在s1找一个最大的 [l,r] 子区间,使其经过从小到大的排序后 能够变成 s2

题解:先确定最小的区间,然后慢慢扩大。

最小区间的确定:s1和s2第一个不相等的数开始,到最后一个不相等的数结尾

向两边扩大区间:如果本来就是从小到大排序的,那么就算sort也无影响,否则停止扩展

import java.util.Scanner;

/*
 *@filename: Demo1
 *@author: lyh
 *@date:2023/4/24 19:09
 *@version 1.0
 *@description TODO
 */
public class Demo1 {
    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        int t=scanner.nextInt();
        while(t-->0){
            int n=scanner.nextInt();
            int[] vis1=new int[n+1];
            int[] vis2=new int[n+1];
            for(int i=1;i<=n;i++){
                vis1[i]=scanner.nextInt();
            }
            boolean flag=true;
            int left=0,right=0;
            for(int i=1;i<=n;i++){
                vis2[i]=scanner.nextInt();
                if(flag&&vis2[i]!=vis1[i]){
                    left=i;
                    flag=false;
                }else if(vis2[i]!=vis1[i]){
                    right=i;
                }
            }
            while(left-1>0){
                if(vis2[left]>=vis2[left-1]){
                    left--;
                }
                else break;
            }
            while(right+1<=n){
                if(vis2[right]<=vis2[right+1]){
                    right++;
                }
                else break;
            }
            System.out.println(left+" "+right);
        }
    }
}

B - Tear It Apart

大意:给定一个字符串,操作多次后只剩下同一字符,每次操作消除不相邻的多个字符,求最小操作次数  

题解:剩下的那个同一字符将字符串分成了多个区间端,就像隔离带一样。并且我们只要处理最长子串就可以了(因为其他的肯定能顺带移除完毕)。

如何寻找该同一字符:遍历字符串每种已有的字母,计算他们分割子串里的最长子串长度,取最小

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

/*
 *@filename: Demo2
 *@author: lyh
 *@date:2023/4/25 14:46
 *@version 1.0
 *@description TODO
 */
public class Demo2 {
    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        int t=scanner.nextInt();
        while(t-->0) {
            String s=scanner.next();
            int length=s.length();
            Map<Character,Integer>map=new HashMap<>(30);
            for(int i=0;i<length;i++){
                if(!map.containsKey(s.charAt(i)))map.put(s.charAt(i),1);
            }
            int minn=Integer.MAX_VALUE;
            for (Map.Entry<Character,Integer>entry:map.entrySet()) {
                  int count=0,temp=0;
                  for(int i=0;i<length;i++){
                      if(s.charAt(i)!=entry.getKey()){
                          count++;
                      }else{
                          temp=Math.max(temp,count);
                          count=0;
                      }
                  }
                  temp=Math.max(temp,count);//最后也要比较一次!
                  minn=Math.min(temp,minn);
            }
            int ans=0;
            while(minn>0){
                minn=minn>>1;//因为不相邻就可以一起消除
                ans++;
            }
            System.out.println(ans);
        }
    }
}

C - Yura's New Name

大意:给出一个仅由 ^ 和 _ 组成的字符串 ,你可以在任意位置添加^或 _ 字符,使得字符串满足:

  • 任意字符要么属于某一子串 ^_^ 的一部分,要么属于某一子串 ^^ 的一部分。

求最少添加的字符数量。

题解:感觉和 括号匹配 是一个题型。开头要求一定得是'^',两个连续的'_'中间需要加'^',以'_'结尾需要加'^'。特殊情况只有一个'^'需要注意判断。

import java.util.ArrayList;
import java.util.Scanner;

/*
 *@filename: Demo2
 *@author: lyh
 *@date:2023/4/25 14:46
 *@version 1.0
 *@description TODO
 */
public class Demo2 {
    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        int t=scanner.nextInt();
        while(t-->0) {
            int ans=0;
            String s=scanner.next();
            int length=s.length();
            if(length==1&&s.charAt(0)=='^'){
                ans++;
                System.out.println(ans);
                continue;
            }
            if(s.charAt(0)!='^')ans++;
            for(int i=0;i<length-1;i++){
                if(s.charAt(i)==s.charAt(i+1)&&s.charAt(i)=='_'){
                    ans++;
                }
            }
            if(s.charAt(length-1)=='_')ans++;
            System.out.println(ans);
        }
    }
}

D - JoJo's Incredible Adventures

大意:有一串由0,1构成的字符串,每次循环右移一位,行编号从0一直到n-1。求这些行里由1构成的最大矩形面积。

题解:  我们其实可以观察到一串连续的1经过右移后是会形成一对正三角和倒三角的,而矩形就在三角内。

我们可以先找到最长的连续字符'1'的串,由于不断右移过程中,连续的 1 的个数可能增加,如下 

我们让s+s(成环),这样遍历就可以找到最长的连续字符'1'的串,设该长为k,矩形的面积可能为1*k、2*(k-1)、3*(k-2)......遍历一遍取最大值即可

注意:特殊情况k等于n,即全是字符'1',这时面积等于n的平方,需要特判,并且需要用long类型接收

import java.util.*;
import java.util.function.Consumer;

/*
 *@filename: Demo2
 *@author: lyh
 *@date:2023/4/25 14:46
 *@version 1.0
 *@description TODO
 */
public class Demo2 {
    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        int t=scanner.nextInt();
        while(t-->0) {
           String s=scanner.next();
           s=s+s;
           long maxL=0,count=0,length=s.length();
           for(int i=0;i<length;i++){
               if(s.charAt(i)=='1'){
                   count++;
                   maxL=Math.max(maxL,count);
               }
               else{
                   count=0;
               }
           }
           if(maxL==length){
               System.out.println((maxL>>1)*(maxL>>1));
               continue;
           }
           long ans=0;
           for(int i=1;i<=(maxL+1)/2;i++){
               ans=Math.max(ans,i*(maxL+1-i));
           }
            System.out.println(ans);
        }
    }
}

E - Constructive Problem

大意: 题意大致为将一个非负整数数组中没有出现过的最小非负整数MEX = x,经过一次操作,该操作为将该数组的一个连续子序列全部变为k,k为非负整数,可以使得该数组的MEX 变成x+1,

例如:0 2 2 0

其中第一个未出现的数为1,要把他变成2,只要将所有的2变为1即可

题解:分两种情况

  1. 如果MEX=n,那么就一定不成立。因为该整数数组是连续并且没有冗余的。如0,1,2,3,4......,每一个数都作出了它的贡献
  2. 如果MEX!=n,那么就一定成立。这说明:小于MEX的数有多余的或者有>=MEX+1的,当然可能两种都有。观察例子可以发现,要让MEX加1,必须要将原来数组中等于MEX+1的数全部消除掉并且变成MEX,并且由于每次操作只能操作一个连续子序列,我们需要记录MEX的最小下标和最大下标。如果数组里没有等于MEX+1的,我们可以在——小于MEX的数有多余的或者>MEX+1的 数随便选一个将其改为MEX,然后检验是否成立
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;

/*
 *@filename: Demo4
 *@author: lyh
 *@date:2023/4/28 1:54
 *@version 1.0
 *@description TODO
 */
public class Demo4 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int t = scanner.nextInt();
        while (t-- > 0) {
            int n = scanner.nextInt();
            List<Integer> list = new ArrayList<>(n);
            for (int i = 0; i < n; i++) {
                int x = scanner.nextInt();
                list.add(x);
            }
            int MEX = getMEX(n, list);
            if (MEX == n) {//0,1,2,3,4,5,6这样
                System.out.println("No");
                continue;
            }
            int minIndex = 0, maxIndex = 0;//MEX的最小下标,和最大下标
            boolean first = false;//为false表示minIndex,maxIndex没有进行第一次初始化
            for (int i = 0; i < n; i++) {
                if (list.get(i) == MEX + 1) {
                    if (!first) {
                        minIndex = i;
                        maxIndex = i;
                        first = true;
                    } else {
                        maxIndex = i;
                    }
                }
            }
            if (!first) {//没有 等于MEX + 1的,那么从>mex+1的或者小于mex的数并且有多余的 拿一个当MEX
                HashSet<Integer> hashSet = new HashSet<>();
                for (int i = 0; i < n; i++) {
                    if (list.get(i) > MEX + 1) {
                        list.set(i, MEX);
                        break;
                    }
                    if (hashSet.contains(list.get(i))) {
                        list.set(i, MEX);
                        break;
                    }
                    else hashSet.add(list.get(i));
                }

            } else {
                for (int i = minIndex; i <= maxIndex; i++) {
                    list.set(i, MEX);
                }
            }
            if (getMEX(n, list) == MEX + 1) {
                System.out.println("Yes");
            } else {
                System.out.println("No");
            }
        }
    }

    public static int getMEX(int n, List<Integer> list) {
        HashSet<Integer> hashSet = new HashSet<>(list);
        int MEX = n;
        for (int i = 0; i < n; i++) {
            if (!hashSet.contains(i)) {
                MEX = i;
                break;
            }
        }
        return MEX;
    }
}

F - Li Hua and Maze 

大意:求最少设置多少个障碍物,可以使起点永远无法到达终点。只能上下左右走。

题解:很简单,封住起点或终点,取封这两个需要的最小障碍物数

import java.util.Scanner;

/*
 *@filename: Demo1
 *@author: lyh
 *@date:2023/4/24 19:09
 *@version 1.0
 *@description TODO
 */
public class Demo1 {
    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        int t=scanner.nextInt();
        while(t-->0) {
            int n = scanner.nextInt();
            int m = scanner.nextInt();
            int x1= scanner.nextInt();
            int y1= scanner.nextInt();
            int x2= scanner.nextInt();
            int y2= scanner.nextInt();
            int getOutStep1=getStep(x1,y1,n,m);
            int getInStep2=getStep(x2,y2,n,m);
            System.out.println(Math.min(getOutStep1, getInStep2));
        }
    }

    public static int getStep(int x1,int y1,int n,int m){
        int ans=0;
        if(x1-1>=1){
            ans++;
        }
        if(x1+1<=n){
            ans++;
        }
        if(y1+1<=m){
            ans++;
        }
        if(y1-1>=1){
            ans++;
        }
        return ans;
    }
}

G - Li Hua and Pattern

大意:李华有一个大小为 n×n 的图案,每个格子为蓝色或红色。他可以进行恰好 k 次操作。每一次操作,他选择一个格子并改变其颜色(红变蓝,蓝变红)。每个格子可以被选择任意多次。是否可能使得操作后的图案中心对称(翻转180度看起来一样)? 

题解:首先,遍历每个点,看它与其中心对称的点相比颜色是否一样,如果不一样则需要翻转一次,因为是中心对称,我们只需要遍历前面一半的点就可以了。特别注意的是如果n为奇数需要特别多遍历一行。

如果修改次数大于k,那肯定不行。如果小于k,分n的奇偶性判断:

  • 若n为奇数,一定可以,因为正中心的小正方形无论翻转多少次都不影响整体,可以消耗多余的操作次数
  • 若n为偶数,剩下多余的次数也要为偶数才行
import java.util.Scanner;

/*
 *@filename: Demo1
 *@author: lyh
 *@date:2023/4/24 19:09
 *@version 1.0
 *@description TODO
 */
public class Demo1 {
    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        int t=scanner.nextInt();
        while(t-->0) {
            int n=scanner.nextInt();
            int k=scanner.nextInt();
            int[][] vis=new int[n+1][n+1];
            for(int i=1;i<=n;i++){
                for(int j=1;j<=n;j++){
                    vis[i][j]=scanner.nextInt();
                }
            }
            int mod=0;
            for(int i=1;i<=(n>>1);i++){
                for(int j=1;j<=n;j++){
                    if(vis[i][j]!=vis[n-i+1][n-j+1])mod++;
                }
            }
            if((n&1)==1){
                for(int i=1;i<=(n>>1);i++){
                    if(vis[(n>>1)+1][i]!=vis[(n>>1)+1][n-i+1])mod++;
                }
                if(mod<=k){
                    System.out.println("YES");
                }else{
                    System.out.println("NO");
                }
            }else{
                if(mod<=k&&((k-mod)&1)==0){
                    System.out.println("YES");
                }else{
                    System.out.println("NO");
                }
            }
        }
    }

}

I - Lucky Numbers

大意:给你两个数,请在该区间找个数,该数已有的数位(个位、十位、百位等)上最大的数-最小的数的差最大

题解:最大也只能是9,暴力求解,一旦发现 最大差 等于9,退出循环,需要注意的是 最大差 在迭代的时候只有>=当前最大差就可以迭代,并记录该数。否则遇到一组完全相等的数或者个位数时无输出,实际上是需要输出一个在区间的数的。


import java.util.Scanner;

/*
 *@filename: Demo3
 *@author: lyh
 *@date:2023/4/27 15:07
 *@version 1.0
 *@description TODO
 */
public class Demo3 {
    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        int t=scanner.nextInt();
        while(t-->0) {
            int left=scanner.nextInt();
            int right=scanner.nextInt();
            int ans=0,cha=0;
            for(int i=left;i<=right;i++){
                int temp=getDifference(i);
                if(temp>=cha){
                   cha=temp;
                   ans=i;
                   if(cha==9)break;
                }
            }
            System.out.println(ans);
        }
     }
     public static int getDifference(int x){
         int maxNum=0;
         int minNum=10;
         while(x>0){
             maxNum=Math.max(maxNum,x%10);
             minNum=Math.min(minNum,x%10);
             x/=10;
         }
         return maxNum-minNum;
     }
}

J - Playing in a Casino

大意:给出一个T组样例,每组样例给出一个n和m表示给出n条数据,每条有m个数据

每次选两条,每个数据一一对应相减取绝对值,求绝对值的和是多少。

题解:首先一眼模拟暴力,但是时间复杂度n立方,果然超时。

我们可以考虑对列排序,因为改变行的位置不会影响结果。经过从小到大排序后,就简化为两两数字差值的问题了。

先假设有一些数字的差值为1,2,3,4,5,我们会发现

每个差值的最后相加的数量=该层及前面的层数*该层有多少个

最后需要注意答案要用long类型接收

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Scanner;

/*
 *@filename: Demo1
 *@author: lyh
 *@date:2023/4/24 19:09
 *@version 1.0
 *@description TODO
 */
public class Demo1 {
    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        int t=scanner.nextInt();
        while(t-->0) {
           int n=scanner.nextInt();
           int m=scanner.nextInt();
            List<List<Integer>> vis=new ArrayList<>();
            for(int i=1;i<=n;i++){
                List<Integer> x=new ArrayList<>();
                for(int j=1;j<=m;j++){
                    int num=scanner.nextInt();
                    x.add(num);
                }
                vis.add(x);
            }
            long sum=0;
            for(int i=0;i<m;i++){
                int finalI = i;//闭包
                vis.sort(new Comparator<List<Integer>>() {//对列排序
                    @Override
                    public int compare(List<Integer> o1, List<Integer> o2) {
                        return o1.get(finalI)-o2.get(finalI);
                    }
                });
                List<Integer>column=new ArrayList<>();
                for(int j=1;j<n;j++){
                    column.add(vis.get(j).get(i)-vis.get(j-1).get(i));
                }
                int size=column.size();
                for(int j=0;j<size;j++){
                    sum=sum+ (long) column.get(j) *(size-j)*(j+1);
                }
            }
            System.out.println(sum);
        }
    }

}

K - Showstopper

大意: 两组数a、b,a1,a2,......an,b1,b2......bn。每次操作可以把对位的an与bn交换,问是否可以经过多次操作使得an和bn(即最后一个数)分别在数组a、数组b中最大

题解:整体是先从末尾出发往前走,首先处理一下末尾:

如果末尾不是最大的,并且对位元素要大一些,那就交换(贪心思想)。

然后往前遍历,如果当前元素大于末尾元素,并且对位元素也大于末尾元素(说明交换也无法挽救),那么就达不到要求。

import java.util.*;

/*
 *@filename: Demo2
 *@author: lyh
 *@date:2023/4/25 14:46
 *@version 1.0
 *@description TODO
 */
public class Demo2 {
    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        int t=scanner.nextInt();
        while(t-->0) {
            int n = scanner.nextInt();
            List<Integer> vis1 = new ArrayList<>();
            List<Integer> vis2 = new ArrayList<>();
            for (int i = 0; i < n; i++) {
                int x = scanner.nextInt();
                vis1.add(x);
            }
            for (int i = 0; i < n; i++) {
                int x = scanner.nextInt();
                vis2.add(x);
            }
            int maxElement1 = Collections.max(vis1);//最大元素
            int maxElement2 = Collections.max(vis2);//最大元素
            if (maxElement1 > vis1.get(n - 1) && vis2.get(n - 1) > vis1.get(n - 1)) {
                int temp = vis1.get(n - 1);
                vis1.set(n - 1, vis2.get(n - 1));
                vis2.set(n - 1, temp);
            } else if (maxElement2 > vis2.get(n - 1) && vis1.get(n - 1) > vis2.get(n - 1)) {
                int temp = vis1.get(n - 1);
                vis1.set(n - 1, vis2.get(n - 1));
                vis2.set(n - 1, temp);
            }
            int pointer = n - 2;
            boolean ans = true;
            while (pointer >= 0) {
                if (vis1.get(pointer) > vis1.get(n - 1)) {
                    if (vis2.get(pointer) > vis1.get(n - 1)||vis1.get(pointer)>vis2.get(n-1)) {
                        System.out.println("NO");
                        ans = false;
                        break;
                    }
                }
                if (vis2.get(pointer) > vis2.get(n - 1)) {
                    if (vis1.get(pointer) > vis2.get(n - 1)||vis2.get(pointer)>vis1.get(n-1)) {
                        System.out.println("NO");
                        ans = false;
                        break;
                    }
                }
                pointer--;
            }
            if(ans) System.out.println("YES");
        }
    }
}

L - Three Sevens

大意:给定m天,每天n个人可以中奖,当前中奖的人不能参加后面的比赛,输出可能的方案

题解:先用一个hashmap存储玩家可能获奖的最近的那一天,然后遍历每一天,把该天可能获奖的玩家编号添加到答案集合里面(一个就可以了),最后判断答案集合长度,若小于n则无法构造。

重点!!!

玛德这题我交了8次才过!都是泪啊

我原本只用了一个hashmap,然后每次都需要遍历他寻找改天可能获奖的玩家,这样太耗时间了

后面我又加了一个HashMap存储 value-key 映射。

这样根据 value 查找 key,时间复杂度为 O(1)!


import java.util.*;

/*
 *@filename: Demo3
 *@author: lyh
 *@date:2023/4/27 15:07
 *@version 1.0
 *@description TODO
 */
public class Demo3 {
    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        int t=scanner.nextInt();
        while(t-->0) {
           int m=scanner.nextInt();
           HashMap<Integer,Integer> playerDay=new HashMap<>(5000);//key为玩家编号,value为天数
           for(int i=1;i<=m;i++){
               int n=scanner.nextInt();
               for(int j=1;j<=n;j++){
                   int x=scanner.nextInt();
                   playerDay.put(x,i);//存储玩家可能获奖的最近的那一天
               }
           }
           List<Integer>ans=new ArrayList<>();
           HashMap<Integer,Integer> dayPlayer=new HashMap<>(5000);//key为玩家编号,value为天数
            // 构建 value-key 映射
            for (Map.Entry<Integer, Integer> entry : playerDay.entrySet()) {
                dayPlayer.put(entry.getValue(), entry.getKey());
            }
           for(int i=1;i<=m;i++){//遍历每一天
               if(dayPlayer.get(i)!=null)ans.add(dayPlayer.get(i));
           }
           if(ans.size()<m) System.out.println(-1);
           else{
               for (int i = 0; i < m; i++) {
                   System.out.print(ans.get(i)+" ");
                   if(i==m-1) System.out.println();
               }
           }
        }
    }
}

N - Sum on Subarrays

大意: 

题解:这题有很多种构造方法,很明显我们要统一用一种排列方式。因为不能出现和为 0 的情况,所以我这里是用2构造,不是用1,并且我们统一把正数放在前面。

设马上要填充的 2是第x个2,每个2会贡献 x种 子串和为正数的情况(它本身,以及和前面的数组合)。这实际上是一个等差数列,上面是刚好填充的情况,如果k是11,那么再加一个2的话,子串和为正的情况会变成15(10+5),这个时候我们需要填充负数,并且需要使组合完只会增加1种 子串和为正的情况。假设前面已经有了n个2,那么这个负数加上前面n个2的和需要>0,但是加上前面n-1个2(第 2 个'2' 至 第 n 个'2')的和需要<0,这样才能保证只会增加1种 子串和为正的情况。

如果构造的情况刚好等于k,此时n个数还没填满,我们后面全部填负无穷(-1000)即可

import java.util.*;

/*
 *@filename: Demo3
 *@author: lyh
 *@date:2023/4/27 15:07
 *@version 1.0
 *@description TODO
 */
public class Demo3 {
    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        int t=scanner.nextInt();
        while(t-->0) {
           int n=scanner.nextInt();
           int k=scanner.nextInt();
           int num2=0;//2的数量
           int contribution=0;
           for(int i=1;i<=n;i++){
               int temp=num2+1;
               if((1+temp)*temp/2<=k){//等差数列求和
                   contribution=(1+temp)*temp/2;
                   num2++;
                   System.out.print(2+" ");
               }else{
                   int difference=k-contribution;
                   //5 8
                   //2 2 2 -3
                   //1 2 3 2
                   int fill=-(num2-difference)*2-1;
                   System.out.print(fill+" ");
                   for(int j=1;j<=n-i;j++) System.out.print(-1000+" ");
                   break;
               }
           }
           System.out.println();
        }
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值