下面是关于CSP-S 2019的错题整理:
解释:B
因为正倒都会一样,所以我们只需要枚举前两位即可,这个题的数据不算很多,我们可以穷举(分别以0,1,8为中间数):
当中间为0
时有:00、06、60、09、90、18、81、66、69、96、99
当中间为1
时有:01、10、16、61、19、91、88
当中间为8
时有:08、80、11、68、86、89、98
所以为25
- 下一个:
解释:C
看选项,因为2^n
永远都不会等于118098
,所以不是。118098
也不是5
的倍数,所以不是。并且也不能整除4
,所以为3
- 下一题:
`#include <cstdio>
using namespace std;
int n;
int a[100];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
int ans = 1;
for (int i = 1; i <= n; ++i) {
if (i > 1 && a[i] < a[i - 1])
ans = i;
while (ans < n && a[i] >= a[ans + 1])
++ans;
printf("%d
", ans);
}
return 0;
}`
解释: 当a
数组是一个严格单调递增的数列时,while()
循环一次都不执行,所以程序的时间复杂度是:O(n)
。
解释:最坏情况下,当a
数组是一个严格单调递减的数列时,while()
会执行n−1,n−2,n−3...
所以程序的时间复杂度是:O(n^2)。
- 下一题:
#include <iostream>
using namespace std;
const int maxn = 1000;
int n;
int fa[maxn], cnt[maxn];
int getRoot(int v) {
if (fa[v] == v) return v;
return getRoot(fa[v]);
}
int main() {
cin >> n;
for (int i = 0; i < n; i++) {
fa[i] = i;
cnt[i] = 1;
}
int ans = 0;
for (int i = 0; i < n - 1; ++i) {
int a, b, x, y;
cin >> a >> b;
x = getRoot(a);
y = getRoot(b);
ans += cnt[x] * cnt[y];
fa[x] = y;
cnt[y] += cnt[x];
}
cout << ans << endl;
return 0;
}
解释:cnt[i]
表示集合中点的个数,若输入的 a 和 b 值均在 [0,n−1]
的范围内, a 和 b 可能已经属于同一集合,若将同一个集合合并两次,那么cnt[i]
中点的数量可能超过n
。
解释:
第 25
行时x
总是不等于 y
,保证了总是将两个不同的集合合并,那么合并过程如下图所示:
每一次从左边向右合并,当a为25时,带入到最左边的点,每一次加上右边点就等于上面的点,经过一系列的激烈运算,就可以得出最上方的点是1225
- 下一题:
本题t
是s
的子序列的意思是:从s
中删去若干个字符,可以得到t。特别的,如果s == t
,那么t也是s的子序列;空串是任何串的子序列。例如“acd
”是“abcde
”的子序列,“acd
”是“acd
”的子序列,但“adc
”不是“abcde
”的子序列。
S[x..y]
表示s[x]…s[y]
共 y−x+1
个字符构成的字符串,若 x>y
则s[x..y]
是空串。t[x..y]
同理。
#include <iostream>
#include <string>
using namespace std;
const int max1 = 202;
string s, t;
int pre[max1], suf[max1];
int main() {
cin >> s >> t;
int slen = s.length(), tlen= t.length();
for (int i = 0, j = 0; i < slen; ++i) {
if (j < tlen && s[i] == t[j]) ++j;
pre[i] = j;// t[0..j-1]是s[0..i]的子序列
}
for (int i = slen - 1, j = tlen - 1; i >= 0; --i) {
if(j >= 0 && s[i] == t[j]) --j;
suf[i]= j; //t[j+1..tlen-1]是s[i..slen-1]的子序列
}
suf[slen] = tlen -1;
int ans = 0;
for (int i = 0, j = 0, tmp= 0; i <= slen; ++i) {
while (j <= slen && tmp >= suf[j] + 1) ++j;
ans = max(ans, j - i - 1);
tmp = pre[i];
}
cout << ans << endl;
return 0;
}
提示:
t[0..pre[i]-1]
是s[0..i]
的子序列;
t[suf[i]+1..tlen-1]
是s[i..slen-1]
的子序列。
解释:
pre[i]
表示s[0..i]
至多可以从前往后匹配到t
串的哪一个字符,此时t[0…pre[i]-1]
是s[0…i]
的子序列。suf[i]
用于记录s[i..slen-1]
至多从后到前匹配到t的哪一个字符,此时t[suf[i]+1..tlen-1]
是s[i..slen-1]
的子序列。本题是求s中连续删除至多几个字母后,t
仍然是s
的子序列
- 从16、17行的代码中可以看出,随着i不断减小,如果
s[i] == t[j]
,j
会减小;否则j
不变,说明suf[i]≤suf[i+1]
- 手动模拟,
s = "a", t = "a"
时,此时ans = 0
。 - 手动模拟,
s = "a", t = "b"
时,j - i - 1
可以为-1
。 - 由含义可知若
t
是s
子序列,t[0..pre[i]-1],t[sub[i+1]+1..lent-1]
是s[0..i],s[i+1..lens-1]
的子序列,不会重叠,即pre[i]−1<suf[i+1]+1
,即pre[i]<=suf[i+1]+1
。 - 若
t
不是s
子串(或t==s)
输出都为0
,但为保证程序执行,最少应输入一个字符。 - 输出为2说明
slen
最多连续删除2
个后为10
,所以最小为12
。
哈哈好难
- 嗯,下一题:
(匠人的自我修养)一个匠人决定要学习 n 个新技术,要想成功学习一个新技术,他不仅要拥有一定的经验值,而且还必须要先学会若干个相关的技术。学会一个新技术之后,他的经验值会增加一个对应的值。给定每个技术的学习条件和习得后获得的经验值,给定他已有的经验值,请问他最多能学会多少个新技术。
输入第一行有两个数,分别为新技术个数 n ,以及已有经验值
接下来 n 行。第 i 行的两个整数,分别表示学习第 i 个技术所需的最低经验值,以及学会第 ii 个技术后可获得的经验值
下面的程序已 O(n^2)的时间复杂完成这个问题,试补全程序。
#include<cstdio>
using namespace std;
const int maxn = 1001;
int n;
int cnt[maxn];
int child [maxn][maxn];
int unlock[maxn];
int threshold[maxn],bonus[maxn];
int points;
bool find(){
int target=-1;
for (int i = 1;i<=n;++i)
if(① && ②){
target = i;
break;
}
if(target==-1)
return false;
unlock[target]=-1;
③
for (int i=0;i<cnt[target];++i)
④
return true;
}
int main(){
scanf("%d%d",&n, &points);
for (int i =1; i<=n;++i){
cnt [i]=0;
scanf("%d%d",&threshold[i],&bonus[i]);
}
for (int i=1;i<=n;++i){
int m;
scanf("%d",&m);
⑤
for (int j=0; j<m ;++j){
int fa;
scanf("%d", &fa);
child [fa][cnt[fa]]=i;
++cnt[fa];
}
}
int ans = 0;
while(find())
++ans;
printf("%d
", ans);
return 0;
}
解释:
程序每次都先学习一个已经达到条件但还未学习的技能,学习后更新经验值和其他技能与该技能有关的学习条件,不断重复至没有技能可以学。unlock
数组为对应技能需学习的前置技能数,大于0
说明有前置技能要学,为−1
表示已学习。
空①,unlock[i] == 0
表示对应技能需学习的前置技能数为0
,即第i
项技能解锁。
空②,要学习第i
项技能,除了第i
项技能已经解锁,还需要由足够的经验值,即points >= threshold[i]
空③,学习了第i项技能,将获得相应的经验值,即points += bonus[target]
。
空④, 学习了第i项技能,还将解锁以第i项技能为前置条件的其它技能,即unlock[child[target][i]] -= 1
空5,初始化第i项技能的前置技能的个数,即unlock[i] = m