蓝桥杯洛谷第一周刷题总结

前言

注:1.P8649 K倍区间答案参考于“晏秋”,P8742 砝码称重答案参考于“一只风里”,感谢两位大佬提供的解法!

2.如有不正确的地方,欢迎指正,谢谢!

前缀和

P8772 求和

import java.util.Scanner;
public class Mian {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner scan = new Scanner(System.in);
		int n = scan.nextInt();//输入整数的个数
		int[] A = new int[n];//用一个数组接收这些整数
		int pre = 0;
		long[] sum = new long[n];
		
		//输入整数
		for(int i = 0;i < n;i++) {
			int num = scan.nextInt();
			A[i] = num;
			pre += num;
			sum[i] += pre;//求出每个整数对应的前缀和
		}
		
		//用前缀和数组进行优化
		//a1*a2 + a1*a3 + ... + a1*an
		//a1*(a2 + a3 + ... +an) = a1*(sum(n) - sum(1))
		long total = 0;//用于接收最终的结果——所求的和
		for(int i = 0;i < n;i++) {
			total += A[i] * (sum[n - 1] - sum[i]);
		}
		System.out.println(total);
		
	}

}

P8649 K倍区间

答案参考:晏秋

import java.util.Scanner;
public class main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner scan = new Scanner(System.in);
		int N = scan.nextInt();
		int K = scan.nextInt();
		int[] A = new int[N];
		long sum = 0;
		long[] B = new long[K];//存放各个余数的个数(这里不太理解为什么要用long)
		for(int i = 0;i < N;i++) {
			A[i] = scan.nextInt();
			sum += A[i];//把前缀和求出来
			B[(int)(sum % K)]++;
		}
		
		long count = 0;//接收结果
		count = B[0];//余数为0的个数(余数为0则必定为K的倍数)
		for(int i = 0;i < K;i++) {
			//两个同余的数相减一定为K倍区间
			//用排列组合公式Cn,即从具有相同余数的前缀和里取出两个前缀和进行相减
			count += (B[i] * (B[i] - 1)) / 2;
		}
		System.out.println(count);
	
	}

}

并查集

基本操作

并查集主要用于不相交集合的合并问题

1.初始化,即将每个节点的祖先节点指向自己

2.查询祖先节点find

3.合并两个节点:将一个节点的祖先节点指向另一个节点的祖先节点

P8654 合根植物

import java.util.Scanner;
public class Main {
	static int[] fa;
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner scan = new Scanner(System.in);
		int m = scan.nextInt();
		int n = scan.nextInt();
		int k = scan.nextInt();
		//用二维数组存放连根数据
		int[][] arr = new int[k][2];
		for(int i = 0;i < k;i++) {
			int a = scan.nextInt();
			int b = scan.nextInt();
			arr[i][0] = a;
			arr[i][1] = b;
		}
		
		//初始化:将每个植物节点的祖先节点指向自己
		//为了方便,数组0位置不使用
		fa = new int[m * n + 1];
		for(int i = 1;i < fa.length;i++) {
			fa[i] = i;
		}
		
		int count = m*n;
		for(int i = 0;i < k;i++) {
			int x_fa = find(arr[i][0]);
			int y_fa = find(arr[i][1]);
			//当x与y的祖先节点相同时,无需将x的祖先节点指向y的祖先节点,count无需减1
			if(x_fa == y_fa) {
				continue;
			}
			else {
				//让x的祖先节点指向y的祖先节点
				fa[x_fa] = y_fa;
				count--;
			}
			
		}
		
		System.out.println(count);
	}
	public static int find(int node) {
		if(fa[node] == node) {
			return node;
		}
		else {
			//比如1→5→6,1的父节点为5,则find(5),即继续寻找5的父节点
			//直到找到最终节点6,6的父节点指向自己,则进入if语句,return 6;所以fa[1] = 6;
			fa[node] = find(fa[node]);//该步进行了路径压缩
			return fa[node];
		}
	}

}

二分查找

P8647 分巧克力

import java.util.Scanner;
public class Main {
		static int N;
		static int K;
		static int[] H;
		static int[] W;
		
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner scan = new Scanner(System.in);
		N = scan.nextInt();
		K = scan.nextInt();
		H = new int[N];//存放各块巧克力的长
		W = new int[N];//存放各块巧克力的宽
		
		for(int i = 0;i < N;i++) {
			int h = scan.nextInt();
			int w = scan.nextInt();
			H[i] = h;
			W[i] = w;
		}
		
		System.out.println(find(1,100000));
		
	}
	
	//二分查找
	public static int find(int l,int r) {
		int ans = -1;
		int mid = 0;
		while(l <= r) {
			mid = (l + r) / 2;
			if(account(mid)) {
				//当以mid为边长分出巧克力块数大于孩子数时
				//继续向右边寻找更大的边长
				ans = mid;
				l = mid + 1;
			}
			else {
				//当以mid为边长分出巧克力块数小于孩子数时
				//向左寻找(将边长缩小一点,快数会多一点)
				r = mid - 1;
			}
		}
		return ans;
	}
	
	public static boolean account(int a) {
		int count = 0;
		for(int i = 0;i < N;i++) {
			count += (H[i] / a)*(W[i] / a);
		}
		if(count >= K) {
			return true;
		}
		else {
			return false;
		}
	}

}

01背包问题

P8742 砝码称重

答案参考:一只风里

import java.util.Scanner;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.HashSet;
public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner scan = new Scanner(System.in);
		int N = scan.nextInt();
		int[] fama = new int[N];//用于存放各个砝码的重量
		for(int i = 0;i < N;i++) {
			fama[i] = scan.nextInt();
		}
		
		HashSet<Long> set = new HashSet<Long>();
		set.add(0L);
		for(int i = 0;i < N;i++) {
            //不能直接使用set的迭代器遍历,迭代器不能及时发现set的更新
            //(自己的理解,如不对请指正,谢谢!)
			ArrayList<Long> list = new ArrayList<Long>(set);
			for(long k:list) {
				set.add(fama[i] + k);
				set.add(Math.abs(k - fama[i]));
			}
		}
		set.remove(0L);
		System.out.println(set.size());
	}

}

深度优先搜索(DFS)

P8605 网络寻路

import java.util.Scanner;
public class Main {
	static int sum;//统计路径
	static int[] node;//用于标记节点是否已访问
	static int[][] map;//用于标记两个节点之间是否有线路
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner scan = new Scanner(System.in);
		int N = scan.nextInt();
		int M = scan.nextInt();
		//为了方便,数组0位置不使用,从1开始
		node = new int[N + 1];
		map = new int[N + 1][N + 1];
		for(int i = 0;i < M;i++) {
			int u = scan.nextInt();
			int v = scan.nextInt();
			map[u][v] = 1;//1表示节点之间有线路
			map[v][u] = 1;
		}
		
		for(int i = 1;i < N + 1;i++) {
			node[i] = 0;//node数组初始化,0表示未访问,1表示已访问
		}
	
		for(int i = 1;i < N + 1;i++) {
			dfs(i,i,0);
		}
		System.out.println(sum);
	}
	//传入三个参数,start表示起始节点,temp表示当前节点,step表示步长(即连接节点的线路条数)
	//1.判断是否为终点,非终点时继续递归调用dfs
	//2.递归调用dfs前需要将当前节点设为已访问,为了防止走回头路;
	//	调用结束后需要将当前节点设为未访问,为了探索其它路径时可以访问该节点
	//此题注意:当步长为2时,需要判断当前节点与起始节点是否有线路,如果有,则sum++
	public static void dfs(int start,int temp,int step) {
		
		if(step == 3) {
			sum++;
			return;
		}
		else if(step == 2) {
			if(map[temp][start] == 1) {
				sum++;
			}
			for(int k = 1;k < map[temp].length;k++) {
				
				if(map[temp][k] == 1 && node[k] == 0) {
					node[temp] = 1;
					
					dfs(start,k,step + 1);
					node[temp] = 0;
				}
			}
				
		}
		else {
			for(int k = 1;k < map[temp].length;k++) {
				
				//如果temp和k之间有线路,且k未访问
				if(map[temp][k] == 1 && node[k] == 0) {
					
					node[temp] = 1;
					//当前节点变为k,步长+1
					dfs(start,k,step + 1);
					node[temp] = 0;
				}
			}
		}
	}

}

有两个测试点未通过,显示超出空间限制,请问大家有什么更好的解吗?

  • 10
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值