九韶杯-题解(Java)

Posted nuist__NJUPT

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了九韶杯-题解(Java)相关的知识,希望对你有一定的参考价值。

目录

A题:6的个数

B题:小明的作业

C题:斐波那契

D题:数列重组

E题:三角形个数

F题:字符串

G题:最强对手矩阵

H题:友谊纽带

I题:传送门

试题J:井字棋残局


A题:6的个数

答案:602

思路:枚举+计数就可以。

public class Main 
    public static void main(String[] args) 
        long ans = 0 ;
        for(int i=1; i<=2021; i++)
            String s = String.valueOf(i) ;
            for(int j=0; j<s.length(); j++)
                if(s.charAt(j)=='6')
                    ans ++ ;
                
            
        
        System.out.println(ans);
    


B题:小明的作业

答案:78   25

思路:枚举所有情况,累计错误和警告数量即可。

public class Main 
    public static void main(String[] args) 
        String homework = "iawaswapwauawhawdwafwanbiopwanivgbik" +
                "vblvbwawawawvolyuvgbololvolgbyolgyowagbolg" +
                "awgboplwawaolgyolwaogblwaygbowawagwabwayawopwawa" +
                "gyowabwaowapjwapcfrtuywawacvujwawawaufttyfuftywawawatifgugbgby" +
                "guwawawawayugbigwwwytigwygwgbwyoawawgoghwaogwborgrewabouyhwabyu" +
                "howabhnwawauygbawyawuwaoawfcawaaaahwaywauwagwawefwaafmbawklawjiawi" +
                "hnwanhawawawawijwajiofjeriofgjrefjhwaewarwaowagwahwauwaiwarwaiwaq" +
                "warwahwaqwawwaowapfweofbwewafwahwaiwaewawwawawawawafwawawawaeiufw" +
                "epfhnewfwahwajwatwafowawajtokshwawafwaiwahwafwahmgoewawawawafkfjke" +
                "wnwawafiewhfwawawafjkernhawkrenwawawawafujnrheiowanwakawawawawwan" +
                "oifewajrwaoawawfweojnwawawawawawawafjkwenawawferkwmpwawawawaforeijaw" +
                "awferhfiueorghwuwafguwegfwaghrwiufgwahweofgowaidwiweaiwwawieyiwe" ;
        long warn = 0, error = 0 ;

        for(int i=0; i<homework.length()-1; i++)
            if(homework.charAt(i) == 'w' && homework.charAt(i+1)=='a' && homework.charAt(i+2) != 'w')
                warn ++ ;
                i ++ ;
            
            if(homework.charAt(i) == 'a' && homework.charAt(i+1)=='w' && homework.charAt(i+2) != 'a')
                warn ++ ;
                i++ ;
            
            if(homework.charAt(i)=='w' && homework.charAt(i+1)=='a' && homework.charAt(i+2)=='w' && homework.charAt(i+3)=='a')
                error ++ ;
                i += 4 ;
                while(homework.charAt(i)=='w' && homework.charAt(i+1)=='a')
                    i += 2 ;
                
                i-- ;
            
            if(homework.charAt(i)=='a' && homework.charAt(i+1)=='w' && homework.charAt(i+2)=='a' && homework.charAt(i+3)=='w')
                error ++ ;
                i += 4 ;
                while(homework.charAt(i)=='a' && homework.charAt(i+1)=='w')
                    i += 2 ;
                
                i--;
            

        
        System.out.println(warn);
        System.out.println(error);
    


C题:斐波那契

答案:6535086616739/3684083162760

思路:这题对Java来说,最需要注意的是要用long,int会爆,本题说白了就是求最大公约数和最小公倍数的问题。

public class Main 
    static long [] f ;
    static long [] ans ;
    public static void main(String[] args) 
        f = new long [14] ;
        ans = new long [14] ;

        f[0] = f[1] = 1 ;
        ans[1] = 1 ;
        for(int i=2; i<14; i++)
            f[i] = f[i-1] + f[i-2] ;
            ans[i] = f[i-1] * f[i] ;
        

        long fenMu = 1 ;
        for(int i=1; i<14; i++)
            fenMu = lcd(fenMu,ans[i]) ;
        

        long fenZi = 0 ;
        for(int i=1; i<14; i++)
            fenZi += (fenMu / ans[i]) ;
        

        System.out.println(fenZi/gcd(fenMu,fenZi) + "/" + fenMu/gcd(fenMu, fenZi));

    

    private static long lcd(long m, long n) 
        return m * n / gcd(m, n) ;
    
    private static long gcd(long m, long n)
        if(n==0)
            return m ;
        else
            return gcd(n, m%n) ;
        
    

D题:数列重组

答案:21456

思路:全排列问题的变种,求全排列,对于每个排列需要找出小于等于两次升降变化的,就是满足要求的,不过本题的数字有重复的,就是可能出现重复的全排列,一般使用抓取法,提前去重,也可以使用交换法全排列之后,再用set集合去重。

抓取法全列代码如下:

import java.util.Arrays;

public class Main 
    static int [] arr =  2,5,3,6,3,6,7,3,7,8 ;
    static long ans = 0 ;
    static boolean [] vis = new boolean [10] ;
    static int [] path = new int [10] ;
    public static void main(String[] args) 
        Arrays.sort(arr) ;
        full(0) ;
        System.out.println(ans);
    
    private static void full(int k)
        if(k==10)
            //升降的变化不得大于2次,否则分不成三个满足要求的
            int num = 0;
            boolean up = false, down = false;
            for (int j = 0; j < 9; j++) 
                if (path[j] < path[j + 1]) 
                    up = true;
                 else if (path[j] > path[j + 1]) 
                    down = true;
                
                if(up && down)
                    num++;
                    up = false;
                    down = false ;
                
            
            if (num <= 2) 
                ans++;
            

        
        for(int i=0; i<10; i++)
            if((i>0 && arr[i] == arr[i-1] && !vis[i-1]))
                continue ;
            
            if(!vis[i]) 
                vis[i] = true;
                path[k] = arr[i];
                full(k + 1);
                vis[i] = false;
            
        
    

交换法全排列,set集合去重代码如下,这个也是正确的,不过超时了,其实填空题运行出结果就可以了。

import java.util.Set;
import java.util.TreeSet;

public class Main 
    static int [] arr = new int [] 2,5,3,6,3,6,7,3,7,8 ;
    static long ans = 0 ;
    static Set<String> set = new TreeSet<>() ;
    public static void main(String[] args) 
        //全排列
        full(0) ;
        System.out.println(ans);
    
    private static void full(int k)
        if(k==10)
            int size = set.size() ;
            String s = "" ;
            for(int element : arr)
                s += element ;
            
            set.add(s) ;
            if(set.size() == size + 1) 
                //升降的变化不得大于2次,否则分不成三个满足要求的
                int num = 0;
                boolean up = false, down = false;
                for (int j = 0; j < 9; j++) 
                    if (arr[j] < arr[j + 1]) 
                        up = true;
                        if (down) 
                            num++;
                            up = false ;
                            down = false;
                        
                     else if (arr[j] > arr[j + 1]) 
                        down = true;
                        if (up) 
                            num++;
                            up = false;
                            down = false ;
                        
                    
                
                if (num <= 2) 
                    ans++;
                
            
        
        for(int i=k; i<10; i++)
            swap(i, k) ;
            full(k+1) ;
            swap(i, k) ;
        
    
    private static void swap(int i, int j)
        int temp = arr[i] ;
        arr[i] = arr[j] ;
        arr[j] = temp ;
    


E题:三角形个数

答案:683228996

思路:找正三角形和倒三角形个数,哈哈,这题好难想,不看别人的题解,确实想不到。


public class Main 
    static int mod = 1000000007 ;
    public static void main(String[] args) 

        long a = 0, n = 20210411 ;
        long ans = 0  ;
        for(long i=1; i<=20210411; i++)
            a = (n - i + 2) * (n - i + 1) / 2 % mod  ;
            if((n-2*i+1)>0) 
                a = (a + (n - 2 * i + 1) * (n - 2 * i + 2) / 2) % mod;
            
            ans = (ans + a) % mod;
        
        System.out.println(ans);
    

F题:字符串

思路:枚举每一行的字符串,截取四个字符,判断是否是@wyk即可。

import java.util.Scanner;

public class Main 
    static long ans = 0 ;
    public static void main(String[] args) 
        Scanner input = new Scanner(System.in) ;
        int N = Integer.parseInt(input.nextLine()) ;

        for(int i=1; i<=N; i++)
            String s = input.nextLine() ;
            for(int j=0; j<s.length(); j++)
                if(s.charAt(j)=='@') 
                    if (s.substring(j, j + 4).equals("@wyk")) 
                        ans++;
                        break;
                    
                
            
        
        System.out.println(ans);

    

G题:最强对手矩阵

思路:这题看一下,立马想到前缀和,不过需要注意的是,小心超时问题。按行求前缀和,枚举上下行和列。AC代码如下:

import java.util.Scanner;

public class Main 
    static int N, M ;
    static int [][] matrix ;
    static int [][] matrix1 ;
    static long max = Long.MIN_VALUE ;
    public static void main(String[] args) 
        //使用前缀和记录
        Scanner input = new Scanner(System.in) ;
        N = input.nextInt() ;
        M = input.nextInt() ;
        matrix = new int [N+1][M+1] ;

        for(int i=1; i<=N; i++)
            for(int j=1; j<=M; j++)
                matrix[i][j] = input.nextInt() ;
            
        

        transpose() ;
        for(int i=1; i<=N; i++)
            for(int j=i; j<=N; j++)
                long now = 0 ;
                for(int k=1; k<=M; k++) 
                    now = Math.max(0,now) + matrix1[j][k] - matrix1[i-1][k] ;
                    max = Math.max(max, now) ;
                
            
        
        System.out.println(max);
    

    private static void transpose() 
        if(N>M)
            matrix1 = new int [M+1][N+1] ;
            for(int i=1; i<=N; i++)
                for(int j=1; j<=M; j++)
                    matrix1[j][i] = matrix[i][j] ;
                
            
            int temp = N ;
            N = M ;
            M = temp ;
        else
            matrix1 = new int [N+1][M+1] ;
            for(int i=1; i<=N; i++)
                for(int j=1; j<=M; j++)
                    matrix1[i][j] = matrix[i][j] ;
                
            
        
        for(int i=1; i<=N; i++)
            for(int j=1; j<=M; j++) //按行求一维前缀和
                matrix1[i][j] += matrix1[i-1][j] ;
            
        
    


第一次写出来的,是按行求出前缀和,然后枚举左上角和右下角的坐标找出最大面积,四层循环,超时了,只能拿一部分的分。代码如下:

import java.util.Scanner;

public class Main 
    static int N, M ;
    static int [][] matrix ;
    static long max = Long.MIN_VALUE ;
    public static void main(String[] args) 
        //使用前缀和记录,枚举计算所有以(row1,col1)为左上角坐标,(row2,col2)为右下角坐标时候的矩形面积
        Scanner input = new Scanner(System.in) ;
        N = input.nextInt() ;
        M = input.nextInt() ;
        matrix = new int [N][M] ;

        for(int i=0; i<N; i++)
            for(int j=0; j<M; j++)
                matrix[i][j] = input.nextInt() ;
            
        

        for(int i=0; i<N; i++)
            for(int j=1; j<M; j++)
                matrix[i][j] += matrix[i][j-1] ; //按行改造成一维前缀和
            
        
        for(int i=0; i<N; i++)
            for(int j=0; j<M; j++)
                for(int m=i; m<N; m++)
                    for(int n=j; n<M; n++)
                        max = Math.max(max, sumRegion(i,j,m,n)) ;
                    
                
            
        
        System.out.println(max);
    
    private static long sumRegion(int row1, int col1, int row2, int col2)
        long sum = 0 ;
        for(int i=row1; i<=row2; i++)
            if(col1>0)
                sum += matrix[i][col2] - matrix[i][col1-1] ;
            else
                sum += matrix[i][col2] ;
            
        
        return sum ;
    

H题:友谊纽带

思路:第一想到的思路是多源最短路,Floyd算法,不过由于是O(n^3)的复杂度,只能通过66.7%的测试用例,后面干脆用搜索,BFS的话,对所有顶点进行搜索,就是每一轮搜索,并标记搜索过的,如果一轮结束,还有没搜索到的,必然不连通,如果搜索到了,num记录当前节点与其余节点的最大值即可。AC代码如下:

import java.util.*;

public class Main 
    static int n, m ;
    static List<Integer>[] edge ;
    static int ans = 0 ;
    static boolean [] vis ;
    static boolean flag = false ;
    public static void main(String[] args) 
       Scanner input = new Scanner(System.in) ;
       n = input.nextInt() ;
       m = input.nextInt() ;
       edge = new List[n] ;
       vis = new boolean [n] ;
       for(int i=0; i<n; i++)
           edge[i] = new ArrayList<>() ;
       

       for(int i=0; i<m; i++)
           int a = input.nextInt() ;
           int b = input.nextInt() ;
           edge[a-1].add(b-1) ;
           edge[b-1].add(a-1);
       
       for(int i=0; i<n; i++)
           vis[i] = true ;
           bfs(i) ;
           for(int j=0; j<n; j++)
               if(!vis[j])
                   flag = true ;
                   System.out.println(-1);
                    return ;
               
           
           Arrays.fill(vis, false) ;
       
      if(!flag)
          System.out.println(ans);
      

    

    private static void bfs(int i) 
        Queue<Integer> queue = new LinkedList<>() ;
        queue.add(i) ;
        int num = -1;
        while(!queue.isEmpty())
            num ++ ;
            int size = queue.size() ;
            for(int j=0; j<size; j++)
                int x = queue.poll() ;
                for(int y : edge[x])
                    if(!vis[y]) 
                        queue.add(y);
                        vis[y] = true;
                    
                
            
        
        ans = Math.max(ans, num) ;
    

Floyd算法代码如下,超时1ms,哈哈,我笑了,菜鸡互啄吧。就是用k节点更新i到j点的最短路。找出每个节点到其余节点的最短路,我用StreamTokenizer类替代Scanner类处理输入,Scanner类太慢了。


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;

public class Main 
    static int n, m ;
    static int [][] dist ;
    static int ans = 0 ;
    static boolean flag = true ;
    public static void main(String[] args) throws IOException 
        StreamTokenizer input = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in))) ;
        input.nextToken() ;
        n = (int) input.nval;
        input.nextToken() ;
        m = (int) input.nval ;
        dist = new int [n][n] ;

        for(int i=0; i<n; i++)
            for(int j=0; j<n; j++)
                    dist[i][j] = n + 1 ;
            
        
        for(int i=0; i<n; i++)
            dist[i][i] = 0 ;
        

        for(int i=0; i<m; i++)
            input.nextToken() ;
            int a = (int) input.nval;
            input.nextToken() ;
            int b = (int) input.nval;
            dist[a-1][b-1] = 1 ;
            dist[b-1][a-1] = 1 ;
        

        for(int k=0; k<n; k++)
            for(int i=0; i<n; i++)
                for(int j=0; j<n; j++)
                        dist[i][j] = Math.min(dist[i][j], dist[i][k] + dist[k][j]);
                
            
        

        for(int i=0; i<n; i++)
            for(int j=0; j<n; j++)
                if(dist[i][j]==n+1)
                    System.out.println(-1);
                    flag = false ;
                    return ;
                else
                    ans = Math.max(dist[i][j], ans) ;
                
            
        
        if(flag)
            System.out.println(ans) ;
        
    


I题:传送门

思路:并查集判断联通性,最小生成树算法,对所有权值升序排序,每次取最小的,如果两个点不连通,则合并,直到取出n-1条边,则说明是联通的,找出最小生成树的最大权值边即是答案。

import java.util.*;

public class Main 
    static int N, M ;
    static long ans = 0 ;
    static int [] fa ;
    static Edges [] edges ;
    static boolean flag = false ;
    public static void main(String[] args) 
        Scanner input = new Scanner(System.in) ;
        N = input.nextInt() ;
        M = input.nextInt() ;
        fa = new int [N+1] ;
        edges = new Edges [M] ;

        for(int i=1; i<=N; i++)
            fa[i] = i ;
        

        for(int i=0; i<M; i++)
            int u = input.nextInt() ;
            int v = input.nextInt() ;
            int w = input.nextInt() ;
            edges[i] = new Edges(u,v,w) ;
        
        solve() ;
        if(flag) 
            System.out.println(ans);
        else
            System.out.println(-1);
        
    

    private static void solve() 
        Arrays.sort(edges, new Comparator<Edges>() 
            @Override
            public int compare(Edges o1, Edges o2) 
                return o1.w - o2.w;
            
        ) ;
        int num = 1 ;
        for(Edges edge : edges)
            int u = edge.u, v = edge.v, w = edge.w ;
            if(find(u) != find(v))
                union(u, v) ;
                num ++ ;
                ans = Math.max(ans, w) ;
                if(num == N)
                    flag = true ;
                    break ;
                
            
        
    
    private static int find(int x)
        if(x != fa[x])
            fa[x] = find(fa[x]) ;
        
        return fa[x] ;
    
    private static void union(int x, int y)
        int root1 = find(x) ;
        int root2 = find(y) ;
        fa[root1] = root2 ;
    


class Edges
    int u, v, w;
    public Edges (int u, int v, int w)
        this.u = u ;
        this.v = v ;
        this.w = w ;
    

思路2;当然直接用并查集也就是可以的,按照权值升序排序,每次两个顶点不是一个集合,则合并,同时找出合并边中的最大值,如果最后所有顶点属于一个集合,说明联通,返回合并边中的最大值即是答案,否则不连通,返回-1.

import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;

public class Main 
    static int N , M, num=0 ;
    static Edge [] edges ;
    static long ans = 0 ;
    static int [] fa ;
    public static void main(String[] args) 
        Scanner input = new Scanner(System.in) ;
        N = input.nextInt() ;
        M = input.nextInt() ;
        fa = new int [N+1] ;
        edges = new Edge[M] ;

        for(int i=1; i<=N; i++)
            fa[i] = i ;
        

        for(int i=0; i<M; i++)
            int u = input.nextInt() ;
            int v = input.nextInt() ;
            int w = input.nextInt() ;
            edges[i] = new Edge(u,v,w) ;
        
        f() ;
        if(num==1) 
            System.out.println(ans);
        else
            System.out.println(-1);
        
    

    private static void f() 
        Arrays.sort(edges, new Comparator<Edge>()  //按权值升序排序
            @Override
            public int compare(Edge o1, Edge o2) 
                return o1.w - o2.w;
            
        );

        for(int i=0; i<M; i++)
            int fu = find(edges[i].u) ;
            int fv = find(edges[i].v) ;
            if(fu!=fv)
                ans = Math.max(ans, edges[i].w) ;
                fa[fu] = fv ;
            
        
        for(int i=1; i<=N; i++)
            if(i==find(i))
               num ++ ;
            
        
    
    private static int find(int x)
        if(x != fa[x])
            fa[x] = find(fa[x]) ;
        
        return fa[x] ;
    

class  Edge
    public int u, v, w;
    public Edge(int u, int v, int w)
        this.u = u ;
        this.v = v ;
        this.w = w ;
    

试题J:井字棋残局

思路:模拟过程就可以,说真话,不太好想。


import java.util.Scanner;

public class Main 
    static int [] arr ;
    public static void main(String[] args) 
        Scanner input = new Scanner(System.in) ;
        int t = input.nextInt() ;
        for(int i=0; i<t; i++)
            int n = input.nextInt() ;
            arr = new int [10] ;
            for(int j=1; j<=n; j++)
                int x = input.nextInt() ;
                int y = input.nextInt() ;
                int idx = (x-1)*3 + (y-1) ; //映射成一维数组
                arr[idx] = (j%2==1) ? 1 : -1 ; //对应的X和O
            
            int ans = dfs(n+1) ;
            if(ans == 1)
                System.out.println("X");
            else
                if(ans==-1)
                    System.out.println("O");
                else
                    System.out.println("-1");
                
            
        
    

    private static int dfs(int x) 
        if(x==10)
            if(check(1))
                return 1 ;
            else if(check(-1))
                return -1 ;
            else
                return 0 ;
            
        else
            int who = (x%2 == 1 ? 1 : -1) ;
            int win = 0, fail = 0, draw = 0 ;
            for(int i=0;i<10;i++)
                if(arr[i]==0)
                    arr[i] = who;
                    if(check(who)) win++;
                    else
                        int res = dfs(x+1);
                        if(res==who)
                            win++;
                        else if(who==-1*res)
                            fail++;
                        else
                            draw++;
                        
                    
                    arr[i] = 0;
                
            
            return win>0?who:(draw>0?0:(-1*who));
        
    

    private static boolean check(int x)  //判断水平垂直和对角线
        for(int i=0;i<3;i++)
            if(arr[i*3]==x && arr[i*3+1]==x && arr[i*3+2]==x) return true;
            if(arr[i]==x && arr[1*3+i]==x && arr[2*3+i]==x) return true;
        
        return (arr[0]==x&&arr[4]==x&&arr[8]==x)||(arr[2]==x&&arr[4]==x&&arr[6]==x);
    

以上是关于九韶杯-题解(Java)的主要内容,如果未能解决你的问题,请参考以下文章

蓝桥杯第十三届Web组国赛天气趋势A详细题解

2020年第十一届蓝桥杯国赛个人题解

第十四届蓝桥杯省赛c/c++大学B组题解

秦九韶公式 多项式

2021软件类第十二届蓝桥杯国赛真题 Python组 A-E题解

第十届蓝桥杯C/C++ B组省赛题解