个人主页:NiKo
算法专栏:算法设计与分析
目录
题目 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;
}