【算法】2022年第十三届蓝桥杯大赛软件类省赛Java大学A组真题

个人主页:NiKo 

算法专栏:算法设计与分析


目录

题目 2685: 蜂巢

题目 2687: 全排列的价值

题目 2667: 青蛙过河

题目 2683: 因数平方和

题目 2689: 最优清零方案

题目 2671: 推导部分和


题目 2685: 蜂巢

  • 题目

  •  题解
import java.util.Scanner;

/*
 * 定义蜂巢中的方向为:
 * 0 表示正西方向,
 * 1 表示西偏北 60◦,
 * 2 表示东偏北 60◦,
 * 3 表示正东,
 * 4 表示东偏南 60◦,
 * 5 表示西偏南 60◦。 
	对于给定的一点 O,我们以 O 为原点定义坐标系,
	如果一个点 A 由 O 点先向 d 方向走 p 步
	再向 (d + 2) mod 6 方向(d 的顺时针 120◦ 方向)走 q 步到达,
	则这个点的坐标定义为 (d, p, q)。
	在蜂窝中,一个点的坐标可能有多种
	
	给定点 (d1, p1, q1) 和点 (d2, p2, q2),
	请问他们之间最少走多少步可以到达?
 */
public class Hive {
	// 1. 将各个方向的单位移动距离 替换位 用新的十字坐标的单位移动距离
	
	// 新的直角坐标系 的单位刻度解释:
	// x 轴: 单位长度 为 1, 蜂巢的任何方向单位刻度都是1, 相同, 以此朝西运动1, 视为运用1个单位
	// y 轴: 单位长度 为 √3/2, 蜂巢的任何方向单位刻度都是1, 因此, 朝西偏北运动1时, y轴上运动√3/2, 但在新坐标体系下, 运动为1个单位
	// 数组的下标表示运动的方向, 数组的值表示在新直角坐标体系下运动的单位数
	public static double[] x = new double[] {-1,-0.5,0.5,1,0.5,-0.5};
	
	// 这里的y 单位长度为 √3/2, 1 表示一个单位长度
	public static double[] y = new double[] {0,1,1,0,-1,-1};
	
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int d1 = in.nextInt();
		int p1 = in.nextInt();
		int q1 = in.nextInt();
		int d2 = in.nextInt();
		int p2 = in.nextInt();
		int q2 = in.nextInt();
		
		// 2. 在新坐标轴下确定两个点的坐标
		double x1= x[d1]*p1 + x[(d1+2)%6]*q1;
		double y1 = y[d1]*p1 + y[(d1+2)%6]*q1;
		
		double x2 = x[d2]*p2 + x[(d2+2)%6]*q2;
		double y2 = y[d2]*p2 + y[(d2+2)%6]*q2;
		
		double deltaX = Math.abs(x1-x2); // x 轴的一个单位 ,可以是斜着2步 也可以是横着1步
		double deltaY = Math.abs(y1-y2); //  y 轴移动一个单位,需要至少斜着1步
		
		// 2. 观察移动规律:
		// 移动只有2种方式
		// 第一种: 斜着移动: 同时来 x 与 y 轴的变化
		// 第二中: 横着移动: 只会带来 x 轴的变化
		// 移动的总步数 = 斜着 + 横着
        int res = (int)deltaY; // 因为具有y轴差, 因此至少需要斜着走的步数
        
        // 由于斜着走的步数 一定会带来 x 轴的变化;
        // 根据 数组对比, 斜着走1步 一定会带来 0.5的x轴变化, 因此
        // 如果 deltaX- deltaY/2 > 0, 说明即使斜着的步数走完后, x轴上依然有差距, 剩下的为x的步数
        if (deltaX- deltaY/2 > 0) {
            res = res + (int)Math.abs(deltaX - deltaY/2);
        }
        System.out.println(res);
	}
}

题目 2687: 全排列的价值

  • 题目

  • 题解 
# 符合题目要求的代码
N = int(input())
 
F = 1
G = 1
 
for n in range(3,N+1):
    p1 = (n*F) %998244353
    G = (G*(n-1)) %998244353
    T = ((n-1)*n/2) %998244353
    p2 = (G*T) %998244353
    F = (p1+p2) %998244353
 
print(int(F))

题目 2667: 青蛙过河

  • 题目

  • 题解 
#include <iostream>
#include <cstring>
#include <algorithm>
#include <stack>
using namespace std;
typedef long long LL;
typedef pair<int,int>pii;
const int mod = 1e9 + 7 , INF = 0x3f3f3f3f , N = 1e5 + 10;
 
int n,m;
int a[N];
 
bool check(int x)
{
    // 如果所有长度为x的区间都大于等于m ,则true
    int s = 0;
    
    for (int i = 1 ; i <= min(n - 1,x) ; i ++)
        s += a[i];
        
    if (s < m)
        return false;
    for (int i = x + 1; i <= n - 1; i ++)
    {
        s -= a[i - x];
        s += a[i];
        if (s < m)
            return false;
    }
    
    return s >= m;
}
 
int main()
{
    cin >> n >> m;
    m *= 2;
    for (int i = 1 ; i <= n - 1 ; i ++)
        cin >> a[i];    
    int l = 0,r = n;
    
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid))
            r = mid;
        else
            l = mid + 1;
    }
    
    cout << r << endl;
    
}

题目 2683: 因数平方和

  • 题解

  • 题解 
import java.util.*;
public class Main{
   public static void main(String []args)
   {final long N=1000000007;
    final long inv6=166666668;
	 Scanner scanner=new Scanner(System.in);
	 long res=0,temp=0,sum=0;
	 long n,l,r,k;
	 n=scanner.nextInt();
	 
	 for(long i=1;i<=n;i=r+1)
	 {
		 l=i;
		 k=n/i;
		 r=n/(n/l);
         //数论分块
		 temp=sum;
		 sum=r*(r+(long)1)%N*((long)2*r+1)%N*inv6%N;
		 res=(res+k*(sum-temp)+N)%N;//sum-temp减去上半截
	 }
	 
	 System.out.println(res);
   }
}

题目 2689: 最优清零方案

  • 题目

  • 题解 
package ___2022年省赛Java大学A组;

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

/**
 * 
 * @author Wzi
 *	给定一个长度为 N 的数列 A1, A2, · · · , AN。
 *	现在小蓝想通过若干次操作将这个数列中每个数字清零。	
 *
 *每次操作小蓝可以选择以下两种之一:
	1. 选择一个大于 0 的整数,将它减去 1;
	2. 选择连续 K 个大于 0 的整数,将它们各减去 1。
 *
 */
public class 最优清零方案_编码计算 {
	private static int n,k ;
	private static int arr[];
	//private static long ans;
	
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		k = sc.nextInt();
		arr = new int[n];
		for(int i=0;i<arr.length;i++) {
			arr[i] = sc.nextInt();
		}
		System.out.println(process(arr,k));
		
	}

	private static  long process(int[] arr, int k) {
		// TODO Auto-generated method stub
		long count = 0;	//通计算了多少次
		int index = 0;	//最初处理位置
		//long ans = 0; //最后用于输出的答案
		long  remind = arr.length;
		while(index <= arr.length -k) {	//小于k则不进行操作了
			long min = Integer.MAX_VALUE;
			int index_way = -1;
			for(int i=index ;i<index+k;i++){	//找出最小值
				if(arr[i] <= min) {
					min = arr[i];
					index_way = i;
				}
			}//for(int i=index ;i<index+k;i++)
			//加速减
			for(int i=index;i<index+k;i++) {
				arr[i]-=min;
			}
			count+=min;
			index=index_way+1;
		}
		
		for(int k1=index;k1<arr.length;k1++) {
			count+=arr[k1];
		}
		return count;
	}//end -private static  long process(int[] arr, int k)
	
		
	
	
}


题目 2671: 推导部分和

  • 题解 
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
const int N=1e5+10;
long long d[N];
int fu[N];
int find(int x)
{
	if(x==fu[x]) return x;
	int t=find(fu[x]);
	d[x]+=d[fu[x]];
	return fu[x]=t;
}
void merge(int x,int y,long long z)
{
	int f1=find(x),f2=find(y);
	if(f1==f2) return ;
	fu[f2]=f1;
	d[f2]=z+d[x]-d[y];
	return ; 
}
int main()
{
	int n,m,q;
	cin>>n>>m>>q;
	for(int i=1;i<=n;i++)
		fu[i]=i;
	for(int i=1;i<=m;i++)
	{
		int l,r;
		long long z;
		scanf("%d%d%lld",&l,&r,&z);
		merge(l-1,r,z);
	}
	while(q--)
	{
		int l,r;
		scanf("%d%d",&l,&r);
		if(find(r)!=find(l-1)) puts("UNKNOWN");
		else printf("%lld\n",d[r]-d[l-1]);
	}
	return 0;
}

评论 42
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NiKo_sx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值