2015年秋季大学先修课考试 解题报告
A:电话费
- 总时间限制:
- 1000ms
- 内存限制:
- 65536kB
- 描述
-
每到月初,小明都会收到中国移动的扣费通知。小明很喜欢打电话,每个月的电话费都很多,所以他去办了优惠套餐。假设小明这个月打了n次电话,小明可以选择不交其中s次电话的话费。小明算术能力很差,请帮帮他算一下这个月最少要交多少话费。
- 输入
- 输入包含两行:第一行为这个月打的电话数n,可以免费的电话数s,其中0 < s,n < 100;第二行包含n个实数,为每次电话的话费。每行中的二个数据之间均已空格间隔。
- 输出
- 输出只有一行,为小明这个月最少要交的话费(保留1位小数)。
- 样例输入
-
7 3 2.0 1.2 3.1 0.6 4.1 1.7 1.2
- 样例输出
- 4.7

#include<iostream> using namespace std; #include<cstdio> #define N 101 int n,s; double a[N]; #include<algorithm> int main() { scanf("%d%d",&n,&s); for(int i=1;i<=n;++i) { scanf("%lf",&a[i]); } sort(a+1,a+n+1); double sum=0; for(int i=1;i<=n-s;++i) sum+=a[i]; printf("%0.1f",sum); return 0; }
B:三个三位数的关系
- 总时间限制:
- 1000ms
- 内存限制:
- 65536kB
- 描述
-
将 1 到 9 这九个数字分成三个 3 位数,要求第一个 3 位数,正好是第二个 3 位数的一半,是第三个 3 位数的三分之一。问该如何分。
- 输入
- 没有输入。
- 输出
- 按第一个数从小到大输出满足条件的方案,每个方案占一行。方案中每个数之间用一个空格分开。
- 样例输入
-
没有输入。
- 样例输出
- 就不告诉大家答案了: )

#include<iostream> using namespace std; #include<cstdio> bool judge(int a,int b,int c) { int flag[10]={0}; if(a<100||b<100||c<100) return false; for(int i=1;i<=3;++i) { int k=a%10; flag[k]++; if(flag[k]>1) return false; a/=10; } for(int i=1;i<=3;++i) { int k=b%10; flag[k]++; if(flag[k]>1) return false; b/=10; } for(int i=1;i<=3;++i) { int k=c%10; flag[k]++; if(flag[k]>1) return false; c/=10; } return true; } int main() { int a; for(int i=300;i<=3000;++i) { if(i%6==0) { a=i/6; int b=2*a; int c=3*a; if(judge(a,b,c)) { printf("%d %d %d\n",a,b,c); } } } return 0; }
C:找到第一个遗漏的实验
- 总时间限制:
- 1000ms
- 内存限制:
- 65536kB
- 描述
-
小王是北京大学医学部的学生。一天,教授让小王帮忙做一系列的医学实验,实验是从1开始标号的,按照自然数顺序,最大不超过100个。每个实验会得到一张实验结果的表格,表格标号就是实验标号。一个月后,小王做完了所有的实验,但是粗心的小王同学遗失了几张实验结果的表格。小王只能把遗失的实验结果重新做一遍,这时,小王想要知道按照从1开始的自然数顺序里,第一个需要补充做的实验是哪个。
- 输入
- 第一行为一个整数n,代表未遗失的所有实验结果表格的数量
第二行为n个整数,代表未遗失实验结果表格的标号 - 输出
- 输出为1行,输出按照自然数顺序,第一个需要重做的实验。
- 样例输入
-
5 2 1 5 10 11
- 样例输出
-
3
- 提示
- 第二行输入类似 1 2 3 4 5这样从1开始的连续的自然数时,代表第6个实验是第一个需要重做的实验,所以应该输出6。

#include<iostream> using namespace std; #include<cstdio> int n; bool flag[200]={0}; int main() { scanf("%d",&n); for(int i=1;i<=n;++i) { int x; scanf("%d",&x); flag[x]=true; } for(int i=1;i<=2*n;++i) if(!flag[i]) { printf("%d\n",i); break; } return 0; }
D:流感传染
- 总时间限制:
- 1000ms
- 内存限制:
- 65536kB
- 描述
-
有一批易感人群住在网格状的宿舍区内,宿舍区为n*n的矩阵,每个格点为一个房间,房间里可能住人,也可能空着。在第一天,有些房间里的人得了流感,以后每天,得流感的人会使其邻居传染上流感,(已经得病的不变),空房间不会传染。请输出第m天得流感的人数。
- 输入
- 第一行一个数字n,n不超过100,表示有n*n的宿舍房间。
接下来的n行,每行n个字符,’.’表示第一天该房间住着健康的人,’#’表示该房间空着,’@’表示第一天该房间住着得流感的人。
接下来的一行是一个整数m,m不超过100. - 输出
- 输出第m天,得流感的人数
- 样例输入
-
5 ....# .#.@. .#@.. #.... ..... 4
- 样例输出
- 16

#include<iostream> using namespace std; #include<cstdio> #define N 150 int jz[N][N]; #include<cstring> int n,m; struct node{ int x,y; }; int xx[]={0,0,1,-1}; int yy[]={1,-1,0,0}; node dl[N*N],dlhb[N*N]; int head=0,tail=0,head1=0,tail1=0; int sum=0; void input() { scanf("%d",&n); for(int i=1;i<=n;++i) { char s[N]; scanf("%s",s+1); for(int j=1;j<=n;++j) { if(s[j]=='.') jz[i][j]=1; if(s[j]=='@') { jz[i][j]=0; ++tail; dl[tail].x=i; dl[tail].y=j; sum++; } if(s[j]=='#') { jz[i][j]=1000000; } } } scanf("%d",&m); } void BFS() { int t=1; while(1) { if(t==m) break; while(head<tail) { ++head; int x1=dl[head].x,y1=dl[head].y; for(int i=0;i<4;++i) { int x2=x1+xx[i],y2=y1+yy[i]; if(x2>=1&&x2<=n&&y2>=1&&y2<=n&&jz[x2][y2]==1) { jz[x2][y2]=0; sum++; ++tail1; dlhb[tail1].x=x2; dlhb[tail1].y=y2; } } } t++; if(t==m) break; while(head1<tail1) { ++head1; int x1=dlhb[head1].x,y1=dlhb[head1].y; for(int i=0;i<4;++i) { int x2=x1+xx[i],y2=y1+yy[i]; if(x2>=1&&x2<=n&&y2>=1&&y2<=n&&jz[x2][y2]==1) { jz[x2][y2]=0; sum++; ++tail; dl[tail].x=x2; dl[tail].y=y2; } } } t++; if(t==m) break; } printf("%d",sum); } int main() { input(); BFS(); return 0; }
E:字符串最大跨距
- 总时间限制:
- 1000ms
- 内存限制:
- 65536kB
- 描述
-
有三个字符串S,S1,S2,其中,S长度不超过300,S1和S2的长度不超过10。想检测S1和S2是否同时在S中出现,且S1位于S2的左边,并在S中互不交叉(即,S1的右边界点在S2的左边界点的左侧)。计算满足上述条件的最大跨距(即,最大间隔距离:最右边的S2的起始点与最左边的S1的终止点之间的字符数目)。如果没有满足条件的S1,S2存在,则输出-1。
例如,S = "abcd123ab888efghij45ef67kl", S1="ab", S2="ef",其中,S1在S中出现了2次,S2也在S中出现了2次,最大跨距为:18。 - 输入
- 三个串:S1, S2, S3,其间以逗号间隔(注意,S1, S2, S3中均不含逗号和空格);
- 输出
- S1和S2在S最大跨距;若在S中没有满足条件的S1和S2,则输出-1。
- 样例输入
-
abcd123ab888efghij45ef67kl,ab,ef
- 样例输出
- 18

#include<iostream> using namespace std; #include<cstdio> #define N 1001 char ss[N+N]; char s[N+10],s1[N/2],s2[N/2]; #include<cstring> int lens,len1=0,len2=0,lenss; void input() { scanf("%s",ss+1); lenss=strlen(ss+1); int i; for(i=1;i<lenss&&ss[i]!=',';++i) s[i]=ss[i]; lens=i; for(i=i+1;ss[i]!=',';++i) { s1[++len1]=ss[i]; } for(i=i+1;ss[i]!='\0';++i) s2[++len2]=ss[i]; } bool judge_1(int l,int r) { for(int i=1;l<=r&&i<=len1;++l,++i) if(s1[i]!=s[l]) return false; return true; } bool judge_2(int l,int r) { for(int i=1;l<=r&&i<=len2;++l,++i) if(s2[i]!=s[l]) return false; return true; } void chuli() { int ans[2]={0}; for(int i=1;i<=lens-len1+1;++i) { if(judge_1(i,i+len1-1)) { ans[0]=i+len1-1; break; } } for(int i=lens-len2+1;i>=1;--i) { if(judge_2(i,i+len2-1)) { ans[1]=i; break; } } if(ans[0]==0||ans[1]==0||ans[0]>ans[1]) printf("-1"); else printf("%d",ans[1]-ans[0]-1); } int main() { input(); chuli(); return 0; }
F:最大零矩阵
- 总时间限制:
- 1000ms
- 内存限制:
- 65536kB
- 描述
-
有一个二位数组 m(< 100)行, n(< 100) 列,其元素为不大于100的非负整数。现要找元素值均为0的最大子二维数组,其中行相邻,列也相邻,行数与列数之积最大(即,所含0元素最多),输出该最大积。例如:
2 5 0 0 8 11 15
3 0 0 0 0 12 16
7 0 0 0 0 13 17
8 0 0 7 1 14 18
4 0 0 0 0 0 0
6 0 0 0 0 0 0
这是6行,7列构成的二维数组,其中:由第4~5行(最后2行),第1~6列(最后6列)构成的子数组最大,共有12个0元素,因此,应该输出 12。其它情况下的子数组都不多于12个0元素,例如,第1~5行与第1~2列构成子数组为第二大数组,含有10个0元素。 - 输入
- 第一行,m 和 n 的值,以空格间隔,m 和 n 均为 不大于 100 的正整数
之后,共 m 行,每行共 n 个元素,其间用空格间格。 - 输出
- 输出,最大零元素子二维数组所含的 0 元素个数,如果没有0元素,则输出0。
- 样例输入
-
6 7 2 5 0 0 8 11 15 3 0 0 0 0 12 16 7 0 0 0 0 13 17 8 0 0 7 1 14 18 4 0 0 0 0 0 0 6 0 0 0 0 0 0
- 样例输出
- 12

#include<iostream> using namespace std; #include<cstdio> #define N 150 int f[N][N]; int h[N],l[N],r[N]; #include<cstring> int n,m; int ans=-1; bool flag=false; void input() { scanf("%d%d",&m,&n); for(int i=1;i<=m;++i) for(int j=1;j<=n;++j) { scanf("%d",&f[i][j]); if(f[i][j]==0) flag=true; } } void chuli() { memset(h,0,sizeof(h)); for(int i=1;i<=m;++i) { int l1,l2; for(int j=1;j<=n;++j) if(f[i][j]==0) h[j]++; else h[j]=0; for(int j=2;j<=n;++j) { if(f[i][j]!=0) continue; l1=j; while(l1>1&&h[l1]>=h[j]) l1--; l[j]=j-l1-1; } for(int j=n-1;j>=1;--j) { if(f[i][j]!=0) continue; l2=j; while(l2<n&&h[l2]>=h[j]) l2++; r[j]=l2-j-1; } for(int j=1;j<=n;++j) ans=max(ans,h[j]*(l[j]+r[j]+1)); } } int main() { input(); if(!flag) { cout<<0<<endl; } else { chuli(); printf("%d",ans); } return 0; }
G:实数加法
- 总时间限制:
- 1000ms
- 内存限制:
- 65536kB
- 描述
-
求2个实数相加的和。
输入输出中出现的实数都有如下的形式: P1P2...Pi.Q1Q2...Qj。对于整数部分,P1P2...Pi是一个非负整数;对于小数部分,至少有一位且最后一位Qj不等于0。
- 输入
- 2行,分别是两个加数。每个加数不超过100个字符。
- 输出
- 一行输出是相应的和。数据保证一定是一个小数部分不为0的实数。
- 样例输入
-
0.111111111111111111111111111111 0.111111111111111111111111111111
- 样例输出
- 0.222222222222222222222222222222
- 代码:

#include<iostream> using namespace std; #include<cstdio> #include<cstring> #define N 150000 int lenint1[N],lenf1[N],lenint2[N],lenf2[N]; int intans[N],lenint,floatans[N],lenfloat; int lenia,lenfa,lenib,lenfb; char ss[N]; void input() { scanf("%s",ss+1); int lens=strlen(ss+1); int i; for(i=1;ss[i]!='.';++i);/*输入的时候,把小数位与整数位分别储存,进行计算*/ lenia=0; for(int j=i-1;j>=1;--j) lenint1[++lenia]=ss[j]-'0';/*小数与整数都要倒序储存*/ lenfa=0; for(int j=lens;j>=i+1;--j) lenf1[++lenfa]=ss[j]-'0'; memset(ss,0,sizeof(ss)); scanf("%s",ss+1); lens=strlen(ss+1); for(i=1;ss[i]!='.';++i); lenib=0; for(int j=i-1;j>=1;--j) lenint2[++lenib]=ss[j]-'0'; lenfb=0; for(int j=lens;j>=i+1;--j) lenf2[++lenfb]=ss[j]-'0'; if(lenfb<lenfa)/*特别注意这个地方:小数的加法不能直接像整数一样加,因为整数是低位对齐加,而小数必须是高位对齐加*/ { int k=lenfa-lenfb;/*这就是对齐的过程,在位数少后面补上0*/ for(int i=lenfb;i>=1;--i) lenf2[i+k]=lenf2[i]; for(int i=1;i<=k;++i) lenf2[i]=0; lenfb=lenfa; } else if(lenfa<lenfb) { int k=lenfb-lenfa; for(int i=lenfa;i>=1;--i) lenf1[i+k]=lenf1[i]; for(int i=1;i<=k;++i) lenf1[i]=0; lenfa=lenfb; } } void add_1() { lenint=1; while(lenint<=lenia||lenint<=lenib) {/*注意这个地方必须是+=才可以,保存之前的进位*/ intans[lenint]+=lenint1[lenint]+lenint2[lenint]; intans[lenint+1]+=intans[lenint]/10; intans[lenint]%=10; lenint++; } if(intans[lenint]==0) lenint--; } void add_2() { lenfloat=1; while(lenfloat<=lenfa||lenfloat<=lenfb) { floatans[lenfloat]+=lenf1[lenfloat]+lenf2[lenfloat]; floatans[lenfloat+1]+=floatans[lenfloat]/10; floatans[lenfloat]%=10; lenfloat++; } if(floatans[lenfloat]==0) lenfloat--; } void out() { if(lenfloat>max(lenfa,lenfb))/*注意二:判断小数是否向整数位进位,也就是总位数加长了*/ { int k=floatans[lenfloat]; lenfloat--; int m=1; intans[m]+=k; while(intans[m]>=10) { intans[m+1]++; intans[m]%=10; m++; } if(intans[lenint+1]!=0) lenint++;/*处理进位的过程*/ for(int i=lenint;i>=1;--i) printf("%d",intans[i]); printf("."); int l=1; while(floatans[l]==0) l++;/*关键:删除小数位后面多余的0*/ for(int j=lenfloat;j>=l;--j) printf("%d",floatans[j]); } else { for(int i=lenint;i>=1;--i) printf("%d",intans[i]); printf("."); int l=1; while(floatans[l]==0) l++; for(int j=lenfloat;j>=l;--j) printf("%d",floatans[j]); } return; } int main() { input(); add_1(); add_2(); out(); return 0; }
H:幼儿园分糖果
- 总时间限制:
- 1000ms
- 内存限制:
- 65536kB
- 描述
-
新年将至,幼儿园的老师们为小朋友们准备了各种各样的糖果。不同的小朋友所喜欢的糖果可能是不同的,为了让更多的小朋友得到他/她喜爱的糖果,幼儿园的老师们对小朋友们的“糖果偏好”(即哪位小朋友喜欢哪几种糖果)进行了统计。
现已知老师们共准备了F种糖果,准备为幼儿园里的N位小朋友分配糖果。同上所述,我们假设:
(1)每位小朋友只会接受自己喜欢的糖果,不接受自己不喜欢的糖果;
(2)每种糖果只能分给某一位小朋友(即:一旦某种糖果分给某位小朋友,则其他小朋友就不能再被分配到该种糖果);
(3)我们不保证所有小朋友都能获得糖果;
(4)每个小朋友喜欢哪种糖果将在输入数据中给出。
请你构造一个程序,帮助老师们分配糖果,以使得在上述条件下,老师们能够将现有糖果分配给最多的小朋友。请输出可分到糖果的小朋友的最多的人数。 - 输入
- 第1行为两个整数:N 和 F,以空格隔开。其中,N(1≤ N ≤ 20)表示小朋友的总人数;F(1 ≤ F ≤ 20)表示糖果的总种数(糖果种类分别用整数1,2,3,...,F进行编号)。
接下来有N行,每行包含多个以空格隔开的整数;其中,第一个整数m,表示某位小朋友所喜爱的糖果的种数,其后的m个整数,表示该小朋友所喜爱的糖果种类的编号序列。
例如:若某行的输入为“3 1 2 3”,则表示该位小朋友共喜欢3种类型的糖果,其糖果类型编号分别为“1”“2”“3”。 - 输出
- 仅一行,即在上述输入条件下,能分到糖果的小朋友的人数的最大值。
- 样例输入
-
4 3 2 1 2 2 2 3 2 1 3 2 1 3
- 样例输出
- 3
- 代码:

/*部分数据不过,需要用二分图匹配*/ #include<iostream> using namespace std; #include<cstdio> #define N 21 #include<algorithm> struct Child{ int sum[N]; bool operator <(const Child &p) const{return sum[0]<p.sum[0];} }child[N]; bool cab[N]={0}; int n,f; void input() { scanf("%d%d",&n,&f); for(int i=1;i<=n;++i) { int m; scanf("%d",&m); for(int j=1;j<=m;++j) { scanf("%d",&child[i].sum[j]); } child[i].sum[0]=m; } } int sum=0; void chuli() { for(int i=1;i<=n;++i) { for(int j=1;j<=child[i].sum[0];++j) { if(!cab[child[i].sum[j]]) { cab[child[i].sum[j]]=true; sum++; break; } } } } int main() { input(); sort(child+1,child+n+1); chuli(); printf("%d\n",sum); return 0; }
I:还是采药问题
- 总时间限制:
- 1000ms
- 内存限制:
- 65536kB
- 描述
- 辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。” 如果你是辰辰,你能完成这个任务吗?
- 输入
- 输入第一行有两个整数T(1 <= T <= 1000)和M(1 <= M <= 100),用一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。接下来的M行每行包括两个在1到100之间(包括1和100)的整数,分别表示采摘某株草药的时间和这株草药的价值。
- 输出
- 输出包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。
- 样例输入
-
70 3 71 100 69 1 1 2
- 样例输出
- 3

#include<iostream> using namespace std; #include<cstdio> #define T 1201 int f[T]; int tim[T],val[T],t,m; void input() { scanf("%d%d",&t,&m); for(int i=1;i<=m;++i) scanf("%d%d",&tim[i],&val[i]); } void DP() { for(int i=1;i<=m;++i) for(int j=t;j>=tim[i];--j) f[j]=max(f[j],f[j-tim[i]]+val[i]); cout<<f[t]<<endl; return ; } int main() { input(); DP(); return 0; }