30 return0;
31 }
判断题
1)(1分)输入的a和b值应在【0,n-1]的范围内。
( )
答案:
对
解析:
从初始化看,下标范围为0~n-1,所以合并范围也在此内
2)(1分)第16行改成“fa[i]=0;”,不影响程序运行结果。
()
答案:
错
解析:
findRoot里用到fa[v]==v表示组长
3)若输入的a和b值均在[0,n-1]的范围内,则对于任意0≤i<n,都有0≤fa[i]<n。
()
答案:
对
解析:
fa[i]表示i同组的上级,下标也在0~n-1范围内
4)若输入的a和b值均在[0,n-1]的范围内,则对于任意≤i<n,都有≤cnt[i]≤n。
()
答案:
对
解析:
cnt表示子连通块大小
选择题
5)当n等于50时,若a、b的值都在[0,49]的范围内,且在第25行时总是不等于y,那么输出为()
A.1276 B.1176 C.1225 D.1250
答案:
C
解析:
每两次合并x和y都不同,表示每次都是单独一个去和整体合并。
此时cnt[y]增加cnt[x]的值,也就是加1。
1*1+1*2+...1*49=50*49/2=1225
6)此程序的时间复杂度是( )
A.O(n) B. O(logn) C.O(n) D.O(nlogn)
答案:
C
解析:
并查集getRoot函数没有路径压缩,单次查找最坏为O(n)。
总效率为O(n^2)
3.本题t是s的子序列的意思是:
从s中删去若干个字符,可以得到t;特别多,如果s=t,那么t也是s的子序列;空串是任何串的子序列。
例如“acd”是“abcde”的子序列,“acd”是“acd”的子序列,但“acd”不是“abcde”的子序列。
S[x..y]表示s[x]…s[y]共y-x+1个字符构成的字符串,若x>y则s[x..y]是空串。
t[x..y]同理。
1 #include
2 #include
3 usingnamespacestd;
4 const int max1=202;
5 strings,t;
6 int pre[max1],suf[max1]
7
8 intmain(){
9 cin>>s>>t;
10 intslen=s.length(),tlen=t.length();
11 for(intI=0,j=0;i<slen;++i){
12 if(j<tlen&&s[i]==t[j])++j;
13 pre[i]=j;//t[0..j-1]是s[0..i]的子序列
14 }
15 for(int I=slen-1,j=tlen-1;I>=0;--i){
16 if(j>=0&&s[i]==t[j])–j;
17 suf[i]=j;//t[j+1..tlen-1]是s[i..slen-1]的子序列
18 }
19 suf[slen]=tlen-1;
20 intans=0;
21. for(inti=0, j=0, tmp=o; i<=slen; ++i){
22. while(j<=slen&&tmp>=suf[j]+1)++j;
23. ans=max(ans,j–I–1);
24. tmp=pre[i];
25. }
26. cout<<ans<<end1;
27. return0;
28. }
提示:
t[0..pre[i]-1]是s[0..i]的子序列;
t[suf[i]+1..tlen-1]是s[i..slen-1]的子序列
判断题
1.(1分)程序输出时,suf数组满足:
对任意0≤i<slen,suf[i]≤suf[i+1].()
答案:
对
解析:
suf[i]是满足t[suf[i]+1..tlen-1]为s[i..slen-1]子序列的最小值
那么t[suf[i+1]+1...tlen-1]是s[i+1..slen-1]的子序列=>t[suf[i+1]+1…tlen-1]也是s[i..slen-1]的子序列,但不是最小(最小值是suf[i]),因此suf[i+1]>=suf[i],单独看15到19行程序也可以直接得出这个结论
2.(2分)当t是s的子序列时,输出一定不为0.()
答案:
错
解析:
可以理解题目的输出:
s中删去连续多少个字母后t仍然是s的子序列;或者直接用s=t='a'代入,结果是0
3.(2分)程序运行到第23行时,“j-i-1”一定不小于0.()
答案:
错
解析:
第一轮执行22行时tmp=0,j=0不执行,因此这轮j-i-1就可能是负数
4(2分)当t时s的子序列时,pre数组和suf数组满足:
对任意0≤i<slen,pre[i]>suf[i+1].()
答案:
错
解析:
可以用简单的样例(如t=s='a')代入检验,也可以根据pre和suf的定义:
如果t是s的子序列,那么0~pre[i]-1,suf[i+1]+1~lent-1这部分分别是s[0~i],s[i+1~lens-1]的子序列,不会重叠,所以有pre[i]-1选择题
5.若tlen=10,输出为0,则slen最小为()
A.10 B.12 C.0 D.1
答案:
D
解析:
slen是s的长度,至少需要输入一个长度的字符串,如果t不是s子序列那输出一定是0
6.若tlen=10,输出为2,则slen最下为()
A.0 B.10 C.12 D.1
答案:
C
解析:
输出是2说明s串删去两个连续元素后t仍是s的子序列,因此删去后长度至少为10,删前至少为12
三、完善程序(单选题,每题3分,共计30分)
1(匠人的自我修养)一个匠人决定要学习n个新技术,要想成功学习一个新技术,他不仅要拥有一定的经验值,而且还必须要先学会若干个相关的技术。
学会一个新技术之后,他的经验值会增加一个对应的值。
给定每个技术的学习条件和习得后获得的经验值,给定他已有的经验值,请问他最多能学会多少个新技术。
输入第一行有两个数,分别为新技术个数n(1≤n≤10³),以及已有经验值(≤10^7).
接下来n行。
第i行的两个整数,分别表示学习第i个技术所需的最低经验值(≤10^7),以及学会第i个技术后可获得的经验值(≤10^4)。
接下来n行。
第i行的第一个数mi(0≤mi<n),表示第i个技术的相关技术数量。
紧跟着m个两两不同的数,表示第i个技术的相关技术编号,输出最多能学会的新技术个数。
下面的程序已O(n^2)的时间复杂完成这个问题,试补全程序。
1 #inclde
2 usingnamesoacestd;
3 constintmaxn=1001;
4
5 intn;
6 intcnt[maxn]
7 intchild[maxn][maxn];
8 intunlock[maxn];
9 intunlock[maxn];
10intthreshold[maxn],bonus[maxn];
11
12boolfind(){
13 inttarget=-1;
14 for(inti=1;i<=n;++i)
15 if(①&&②){
16 target=i;
17 break;
18 }
19 if(target==-1)
20 returnfalse;
21 unlock[target]=-1;
22③;
23 for(inti=0;i<cut[target];++i)
24④;
25returntrue;
26 }
27
28 intmain(){
29scanf('%d%d',&n,&points);
30for(intI=1;i<=n;++i={
31 cnt[i]=0;
32 scanf('%d%d',&threshold[i],&bonus[i];
33 }
34 for(inti=1;i<=n;++i={
35 intm;
36 scanf('%d',&m);
37 ⑤;
38 for(intj=0;j<m;++j={
39 intfa;
40 scanf('%d',&fa);
41 child[fa][cnt[fa]]=i;
42 ++cnt[fa];
43 }
44 }
45 intans=0;
46 while(find())
47 ++ans;
48 printf('%d\n',ans);
49 return0;
50 }
1)①处应填( )
A. unlock[i]<=0
B. unlock[i]>=0
C. unlock[i]==0
D.unlock[i]==-1
答案:
C
解析:
unlock作用是看是否能解锁任务。
根据对问题5的分析,在未解锁前它的值是还有几个依赖任务未解锁。
那么解锁条件当然是0个依赖任务,因此是等于0
2)②处应填( )
A.threshold[i]>points
B.threshold[i]>=points
C.points>threshold[i]
D.points>=threshold[i]
答案:
D
解析:
很简单,解锁条件之二,经验点要大于等于任务的需求点
3)③处应填( )
A.target=-1
B.--cnt[target]
C.bbonus[target]
D.points+=bonus[target]
答案:
D
解析:
经验点增加。
A肯定不对,target后面还要用。
B不对,因为cnt[i]是依赖i的任务。
C也不对,bonus是只读的
4)④处应填( )
A.cnt[child[target][i]]-=1
B.cnt[child[target][i]]=0
C.unlock[child[target][i]]-=1
D.unlock[child[target][i]]=0
答案:
C
解析:
从前面分析看出unlock是依赖的还没