第十六届蓝桥杯省赛JavaB组题解
A 逃离高塔

第一道填空题很简单,根据题意跑一边循环即可,一共是202个符合条件的数
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int ans=0;
for(long i=0;i<=2025;i++){
if((i*i*i)%10==3)
ans++;
}
System.out.println(ans);
}
需要注意的是循环变量要定义成long型,不然三次方会爆int,只会得到153个数字
C电池分组

要将数组分成两块,并使它们的异或和相同,直接暴力求解是很繁琐的
异或是同一个比特位上两个数相同则为0,不同为1,要找出异或和相同的两组数,假设已经找到这两组数了,这个时候两个异或和是相同的,对他们做异或运算结果是0
也就是说,假设a^b^c 和d^e是我们找到的两组异或和相同的两组数,那么(a^b^c)^(d^e)=0
而且异或运算满足交换律,所以我们直接对输入的所有数字做异或运算,看最后的结果如果为0就代表满足题意
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int t=0;
t=scanner.nextInt();
int x=0;
while((t--)>0){
int n = scanner.nextInt();
x=0;
for(int i=0;i<n;i++){
x^=scanner.nextInt();
}
if(x==0){
System.out.println("YES");
}else{
System.out.println("NO");
}
}
}
}
不过我在考场上第一时间没有想到这个思路,我是后面回过头来在看的这道题目,我观察了一下题目,发现当某个比特位上出现1的个数为偶数时符合题意,为奇数时则不符合题意,所以我就统计所有数每个比特位上1的个数,如果某个比特位上1的个数为奇数则直接输出NO,否则就是符合题意的,考完后仔细想了一下,也算是歪打正着做对了
当某个位上出现1的个数为偶数个时,这些1的异或和是0,和剩下的0在做异或结果也是0
当某个位上出现1的个数为奇数个时,这些1的异或和是1,和剩下的0在做异或结果为1,那么最后所有数的异或和绝不会为0
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int t=0;
t=scanner.nextInt();
int x=0;
//存储每个比特位上1的个数
int[] bit=new int[32];
int cnt=0;
boolean istrue=true;
while((t--)>0){
int n = scanner.nextInt();
istrue=true;
//清空上次结果
for(int i=0;i<32;i++){
bit[i]=0;
}
for(int i=0;i<n;i++){
cnt=0;
x=scanner.nextInt();
while(x>0){
if((x&1)>0){
bit[cnt]++;
}
x>>=1;
cnt++;
}
}
for(int i=0;i<32;i++){
if(bit[i]%2==1){
istrue=false;
}
}
if(istrue){
System.out.println("YES");
}else{
System.out.println("NO");
}
}
}
}
D魔法科考试

这题没想到太好的思路,直接提前做了一个素数筛,然后两层循环暴力求解
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
//素数筛
boolean[] isz=new boolean[40010];
isz[1]=true;
for(int i=2;i*i<=40001;i++){
if(!isz[i]){
for(int j=i+i;j<=40001;j+=i){
isz[j]=true;
}
}
}
int n=scanner.nextInt();
int m=scanner.nextInt();
int[] a=new int[n];
int[] b=new int[m];
for(int i=0;i<n;i++){
a[i]=scanner.nextInt();
}
for(int i=0;i<m;i++){
b[i]=scanner.nextInt();
}
int s=0,sum=n+m,ans=0;
boolean[] vis=new boolean[sum+1];
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
s=a[i]+b[j];
if(s<=sum&&!isz[s]&&!vis[s]){
ans++;
vis[s]=true;
}
}
}
System.out.println(ans);
}
}
E爆破

刚开始看见又是圆心又是半径的,直接就跳过了,看完后面回过头来发现这就是一个最小生成树
直接暴力选边是不可取的,时间复杂度高不说,无法保证每个圆都连通,题目提到如果两个魔法阵相交,可以一起引爆,可以自己选择连边让孤立的圆与其他圆连接,当时就直接想到了最小生成树
注意,题目中所说两个魔法阵相交的意思是圆心之间的距离小于等于半径之和
解题思路是,将坐标系当作一个图,我们将每个圆当作图中的一个点,以输入的下标当作圆的编号,开始时假设每个圆与其他所有圆都有一个无向边,边长是两圆心之间的距离减去半径之和,也就是想让这两个圆相交的最小代价,假设一共n个圆,那么就有n*(n-1)/2条边,存进数组中根据边长排序,后面利用并查集从小到大选边
相交的两个圆的边长是负数,在处理时不用计入答案,标记后直接跳过就好
(忘了考虑边的数量问题了,以我的思路这题就成了一个完全图,数据量过大会超时并内存超限,只能过一部分样例)

import java.util.*;
public class Main {
public static class edge{
private int x;
private int y;
private double r;
public edge(int x, int y, double r){
this.x = x;
this.y = y;
this.r = r;
}
}
private static int[] father;
private static int n,m;
//初始化
public static void init(){
father = new int[n+1];
for(int i=0;i<father.length;i++){
father[i] = i;
}
}
//查找父节点,同时更新父节点信息
public static int find(int x){
return father[x]==x?x:(father[x]=find(father[x]));
}
public static double kruskal(edge[] edges){
//初始化父亲数组
init();
double ans=0;
int fx=0,fy=0;
for(edge e:edges){
fx=find(e.x);
fy=find(e.y);
if(fx!=fy){
father[fx]=fy;
//大于0时计入答案
if(e.r>0){
ans+=e.r;
}
}
}
return ans;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
n = scanner.nextInt();
m=n*(n-1)/2;
int cnt=0;
int[] x = new int[n];
int[] y = new int[n];
int[] r = new int[n];
edge[] edges = new edge[m];
for(int i=0;i<n;i++){
x[i] = scanner.nextInt();
y[i] = scanner.nextInt();
r[i] = scanner.nextInt();
}
double line=0;
int lx=0,ly=0;
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
lx=x[i]-x[j];
ly=y[i]-y[j];
line=Math.sqrt((lx*lx)+(ly*ly))-r[i]-r[j];
edges[cnt]=new edge(i,j,line);
cnt++;
}
}
//按边的长度从小到大排序
Arrays.sort(edges,new Comparator<edge>(){
public int compare(edge e1, edge e2) {
double x=e1.r-e2.r;
if(x<0) return -1;
if(x>0) return 1;
return 0;
}
});
double ans=kruskal(edges);
System.out.printf("%.2f",ans);
}
}
F数组翻转

这题使用map针对每个数字维护最大的两个连续序列,遍历一遍数组即可,最后查看哪个数字的两个连续序列值的和最大
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n=scanner.nextInt();
int[] arr=new int[n];
for(int i=0;i<n;i++){
arr[i]=scanner.nextInt();
}
Map<Integer,long[]> map=new HashMap<>();
long sum=arr[0];
int now=arr[0];
long[] x=null;
for(int i=1;i<n;i++){
if(now!=arr[i]){
x=map.get(now);
if(x==null){
x=new long[2];
map.put(now,x);
}
if(x[0]==0){
x[0]=sum;
}else if(x[1]==0){
x[1]=sum;
}else{
if(x[0]>x[1]){
if(sum>x[1]){
x[1]=sum;
}
}else{
if(sum>x[0]){
x[0]=sum;
}
}
}
now=arr[i];
sum=arr[i];
}else{
sum+=arr[i];
}
}
x=map.get(now);
if(x==null){
x=new long[2];
map.put(now,x);
}
if(x[0]==0){
x[0]=sum;
}else if(x[1]==0){
x[1]=sum;
}else{
if(x[0]>x[1]){
if(sum>x[1]){
x[1]=sum;
}
}else{
if(sum>x[0]){
x[0]=sum;
}
}
}
int key=0;
long[] value=null;
long ans=0;
for(Map.Entry<Integer,long[]> entry:map.entrySet()){
key=entry.getKey();
value=entry.getValue();
ans=Math.max(ans,value[0]+value[1]);
}
System.out.println(ans);
}
}
G题真不会,忘了骗个分了
H研发资源分配

这题我采用贪心的思想去做的,赢得越靠后的竞争获得的分数越大,所以我们从后向前遍历
对于当前的B部门的分值bi,查看A部门的bi+1是否使用,如果没使用我们就可以拿最小的代价赢得这个分数i+1,如果A部门的bi+1已经使用过了,再看是否可以打成平局,如果还是不行就拿一个尚未使用的最小分数去应付,有点像田忌赛马了,洛谷上面这个思路是能够ac的,不过我忘了开long了,int直接爆掉,只能过一部分样例,开long可以全对
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n=scanner.nextInt();
int[] b=new int[n];
for(int i=0;i<n;i++)
b[i]=scanner.nextInt();
long ans=0;
boolean[] vis=new boolean[n+1];
for(int i=n-1;i>=0;i--){
if(b[i]!=n&&!vis[b[i]+1]){
vis[b[i]+1]=true;
ans+=i+1;
}else if(!vis[b[i]]){
vis[b[i]]=true;
}else {
for(int j=1;j<=n;j++){
if(!vis[j]){
vis[j]=true;
break;
}
}
ans-=i+1;
}
}
System.out.println(ans);
}
}
更多推荐

所有评论(0)