A - Codeforces Checking [Easy] 题解
B - Following Directions [Easy] 题解
C - Prepend and Append [Easy] 题解
D - Distinct Split [Easy] 题解
E - Negatives and Positives [Easy] 题解
F - Range Update Point Query [Easy] 题解
G1 - Teleporters (Easy Version) [Medium] 题解
G2 - Teleporters (Hard Version) [Hard] 题解
A - Codeforces Checking
就是给你一个字符,判断它是不是在单词“codeforces”里。
思路
暴力
- 时间复杂度:O(n)
- 空间复杂度:O(1)
代码
1 | def solve(): |
B - Following Directions
给你一个字符串表示移动方向,看移动过程中有没有经过点(1, 1)。
思路
数据量很小,直接模拟;到了点(1, 1)就return true。
- 时间复杂度:O(n)
- 空间复杂度:O(1)
代码
1 | def solve(): |
C. Prepend and Append
有一个操作每次可以将字符串头部和尾部加上0、1或1、0。给你一个字符串,返回可以经过任意次操作可得到它的最短字符串。
思路
反向思考,每次操作删去首尾0、1。直到首尾为0、0或1、1或字符串为空。
- 时间复杂度:O(n)
- 空间复杂度:O(1)
代码
1 | def solve(): |
D. Distinct Split
让我们把一个字符串x的f(x)函数表示为该字符串包含的不同字符的数量。例如f(abc)=3, f(bbbb)=1和f(babacaba)=3。
给定一个字符串ss,把它分成两个非空字符串a和b,使f(a)+f(b)为最大可能值。换句话说,找出f(a)+f(b)的最大可能值,使a+b=ss。
思路
枚举分割点,分别维护两边不同字符的数量。
考虑到Set比较耗时,可以用两个数组来记录出现不同字符的数目:增加后字符数量为1就+1,删除后字符数量为0就-1。
- 时间复杂度:O(n)
- 空间复杂度:O(1)
代码
1 | def solve(): |
E. Negatives and Positives
给定一个由n个元素组成的数组a,求该数组在执行以下任意次数的操作后可能具有的最大和。
选择2个相邻的元素,翻转它们的符号。换句话说,选择一个索引i,使1≤i≤n-1,并指定ai=-ai,ai+1=-ai+1。
思路
一眼看去像是贪心,但不敢贪😅;这题动态规划思路很好像,还是老老实实DP。
对于每个数而言,只有两种可能情况:翻转和不翻转(和背包问题很像,可以看看这篇文章这篇文章)。
于是定义dp[i][0]:不翻转第i个数可以得到的最大值 dp[i][1]翻转第i个数可以得到的最大值:
递推:不反转就直接把第i个数和前面可以得到的最大值相加;否则第i个数和第i-1个数也要翻转。
dp[i-1][0]
表示i-1个数没有翻转,此时dp[i-1][0]-a[i-1]
就是前i-2个数最大值,再减去a[i-1]和a[i]
就行。
dp[i-1][1]
表示i-1个数没有翻转,此时dp[i-1][0]+a[i-1]
就是前i-2个数最大值,再减去-a[i-1]和a[i]
就行。(因为此时啊a[i-1]
翻转过了,变成了-a[i-1]
)
- 时间复杂度:O(n)
- 空间复杂度:O(n)
代码
1 | def solve(): |
Tips
dp[1][0], dp[1][1] = a[0], int(-1e18)
初始化要思考下。单独翻转第一个数是不可能的,所以要设置为无穷小。(比可能的最小值小就行了,一开始写成-1e9,怒wa)
F. Range Update Point Query
给定一个数组a1,a2,……,an,你需要处理两类共计q的更新和查询。
1 l r - 对于每个有l≤i≤r的索引i,将ai的值更新为ai的各个位数上的数字之和。
2 x - 输出a[x]。
思路
看到题干,一眼线段树。但这题只涉及区间修改和单点查询,用树状数组更简单。两种数据结构详见数据结构
还要注意所有可能的数最多更新三次就不会变了。
- 时间复杂度:O(nlog n)
- 空间复杂度:O(n)
代码
1 | static int n, m, a[] = new int[N], tr[] = new int[N]; |
Tips
多组测试用例的题目,对于全局变量一定要
初始化
!!!即
Arrays.fill(a, 0); Arrays.fill(tr, 0);
,不然会死的很惨。
G1. Teleporters (Easy Version)
G1 - Teleporters (Easy Version)
考虑到数线上的点0,1,…,n。在每一个点1,2,…,n上有一个传送器。在i点,你可以做以下事情。
向左移动一个单位:花费1枚硬币。
向右移动一个单位:需要花费1个硬币。
在第i点使用一个传送器,如果它存在的话:它需要花费ai硬币。结果,你会被传送到0点。
一旦你使用了一个传送器,你就不能再使用它了。
你有c硬币,你从0点开始。你最多可以使用多少个传送器?
思路
可以发现,每次到一个点传送后都回到原点。即到达每个点使用传送器的总消耗是独立的,等于 到该点的花费+该店使用传送的花费。
即预处理所有点的花费,从小到大排序,看最多能支持几次。(其实可以前缀和+二分,但懒😅)
- 时间复杂度:O(nlog n)
- 空间复杂度:O(n)
代码
1 | def solve(): |
G2. Teleporters (Hard Version)
G2 - Teleporters (Hard Version)
与EASY版本不同的是使用传送后,可以回到点0,也可以回到点n+1。
思路
与EASY版本一样,如果我们不考虑第一个去的点,问题对每个点来说仍然是独立的,但这一次门户的成本是min(a[i]+i,a[i]+n+1-i)
(因为我们可以从0点或n+1点来到一个门户)。因此,我们再次按成本对传送门进行排序。但是这一次,我们需要确保第一个拿下的传送门是从0点去的,所以我们将遍历所有的点,并检查如果我们把它作为第一个传送门,我们能拿下的最大传送门数量。
这里就需要用二分找到每次可以去的点的最大值。(与常规二分不同,需要单独对当前遍历到的点处理)
- 时间复杂度:O(nlog n)
- 空间复杂度:O(n)
代码
1 | static int n, c; |