【纪念第一次AK(泪目)】第一届ACC(AcWing Cup)全国高校联赛(初赛)(Java版)

一、4376. 数圈圈(简单)

在这里插入图片描述
暴力就行,唯一要注意的是Java的Integer.toString(),会把十六进制的A、B、C…转成小写!!!(被罚时了)

import java.util.*;
import java.io.*;

public class Main {
	static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
	static BufferedWriter log = new BufferedWriter(new OutputStreamWriter(System.out));
	public static void main(String[] args) throws IOException {
		Scanner scan = new Scanner(System.in);
		int n = scan.nextInt();
		String tmp = Integer.toString(n, 16);
		char[] str = tmp.toCharArray();
		int cnt = 0;
		for (char cur : str) {
			if (cur == '0' || cur == '4' || cur == '6' || cur == '9' || cur == 'a' || cur == 'd') {
				cnt++;
			} else if (cur == '8' || cur == 'b') {
				cnt += 2;
			} 
		}
		System.out.println(cnt);
	}
}

二、4377. 农田灌溉(中等)

在这里插入图片描述

在这里插入图片描述
有了这周周赛的锻炼,对于这种区间问题终于是有思路了。

在这里插入图片描述
如上图分析所示,外层循环遍历农田,内层循环遍历所有可能的洒水器,只需要找距离当前农田最近的洒水器即可。

import java.util.*;
import java.io.*;

public class Main {
	static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
	static BufferedWriter log = new BufferedWriter(new OutputStreamWriter(System.out));
	public static void main(String[] args) throws IOException {
		String[] input = reader.readLine().trim().split(" ");
		int t = Integer.parseInt(input[0]);
		while (t-- > 0) {
			input = reader.readLine().trim().split(" ");
			int n = Integer.parseInt(input[0]);  // n片农田
			int k = Integer.parseInt(input[1]);  // k个装有洒水器的农田
			boolean[] have = new boolean[n + 1];
			input = reader.readLine().trim().split(" ");
			for (int i = 0; i < k; i++) {
				// 标记有洒水器的地方
				have[Integer.parseInt(input[i])] = true;
			}
			int ans = 1;
			for (int i = 1; i <= n; i++) {
				if (have[i]) {
					continue;
				}
				int min = Integer.MAX_VALUE;
				// 没有喷水器,去找最近的喷水器
				for (int j = 1; j <= n; j++) {
					if (have[j] && Math.abs(j - i) < min) {
						min = Math.abs(j - i);
					}
				}
				ans = Math.max(ans, min + 1);
			}
			log.write(ans + "\n");
		}
		log.flush();
	}
}

三、4378. 选取数对(困难)

在这里插入图片描述
在这里插入图片描述
一开始的思路是搜索,超时了,搜索可以转成DP,所以最后用DP完成了最后一击,成功AK。

题目意思:需要找到 k 个 长度为 m 的数对,每个数对不能包含重复的元素,要求所有数对的和的最大值。长度为 m 的数对,也就是长度为 m 的区间和,也就是需要用preSum去找区间和,因为数对长度必须为 m,所以可以从第 m 个数开始枚举,对于当前数对,可以选择、不选,选的话,下标就得跳到 i + m,如果不选的话下标跳到 i + 1,最后找完所有的 k 个数对,求最大值即可,用DFS很容易做:

import java.util.*;
import java.io.*;

public class Main {
	static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
	static BufferedWriter log = new BufferedWriter(new OutputStreamWriter(System.out));
	static long[] preSum;
	static int n, m, k;
	static long max = 0;
	public static void main(String[] args) throws IOException {
		String[] input = reader.readLine().trim().split(" ");
		n = Integer.parseInt(input[0]);
		m = Integer.parseInt(input[1]);
		k = Integer.parseInt(input[2]);
		preSum = new long[n + 1];
		input = reader.readLine().trim().split(" ");
		for (int i = 1; i <= n; i++) {
			preSum[i] = preSum[i - 1] + Long.parseLong(input[i - 1]);
		}
		// k个数对,数对长度为m(前缀和长度),和最大
		dfs(m, 0, 0);  // 从第m个下标开始搜(才能确保长度为m),数对个数 = 0
		log.write(max + "");
		log.flush();
	}
	static void dfs(int index, int cnt, long cur) {
		if (cnt == k) {
			// 找到了k对数
			max = Math.max(max, cur);
			return;
		}
		if (index > n) return;
		// 保证数对长度
		long tmp = preSum[index] - preSum[index - m];
		dfs(index + m, cnt + 1, cur + tmp);
		// 也可以不要这个数
		dfs(index + 1, cnt, cur);
	}
}

只过了13个数据,显然是因为数据量太大,超时了。

仔细想想,当前数对选、不选,都会对后面的答案产生影响,而我们要最大值,后面的答案可以由前面已有的结果推出,所以可以用DP数组进行求解。

直接写出DP还是有难度,但是从DFS转到DP就简单了很多,因为你知道到底在搜索什么东西,也就是DP需要记录的状态。

import java.util.*;
import java.io.*;

public class Main {
	static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
	static BufferedWriter log = new BufferedWriter(new OutputStreamWriter(System.out));
	static long[] preSum;
	static int n, m, k;
	static long max = 0;
	public static void main(String[] args) throws IOException {
		String[] input = reader.readLine().trim().split(" ");
		n = Integer.parseInt(input[0]);
		m = Integer.parseInt(input[1]);
		k = Integer.parseInt(input[2]);
		preSum = new long[n + 1];
		input = reader.readLine().trim().split(" ");
		for (int i = 1; i <= n; i++) {
			preSum[i] = preSum[i - 1] + Long.parseLong(input[i - 1]);
		}
		// dp[i][j]: 前 i 个数,在 j 个数对的情况下的最大数对和
		long[][] dp = new long[n + 1][k + 1];
		for (int i = m; i <= n; i++) {  // 从m开始,避免第一个数对不满足题意
			// 对于当前一个数对,可以考虑其初始值:preSum[i] - preSum[i - m]
			dp[i][1] = preSum[i] - preSum[i - m];
			for (int j = 1; j <= k; j++) {
				long tmp = preSum[i] - preSum[i - m];
				// 当前数对可以不选:dp[i - 1][j],选:dp[i - m][j - 1] + tmp,选的话不能和前面的选择重复,所以必须得跳到i - m
				dp[i][j] = Math.max(dp[i - 1][j], dp[i - m][j - 1] + tmp);
			}
		}
		log.write(dp[n][k] + "");
		log.flush();
	}
}

BTW,没想到这一次能够AK,第三题看着DFS超时,心态就有点崩,但静下来认真思考,终于在最后转成了DP,泪目!!纪念!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

@u@

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

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

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

打赏作者

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

抵扣说明:

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

余额充值