WHUT迎新入门赛整理
写在前面的话:我的能力也有限,错误是在所难免的!因此如发现错误还请指出一同学习!
索引
一、1001~1008:控制输入输出
二、1009和1012:高精的使用
三、1010:最大连续区间和(经典dp)
四、1011:规律题
一、1001~1008:控制输入输出
1001:A+B问题,不确定数据的组数
处理方法:处理到文件末尾,即EOF
C语言版:
1.scanf函数的返回值是输入的个数,当读入到文件末尾EOF的时候scanf的返回值是-1。
int a, b;
while(scanf("%d%d", &a, &b) == 2){
printf("%d\n", a+b);
}
2.因为scanf读到EOF的返回值是-1,而-1在计算机的补码表示中为11111111二进制串,“~”操作符是按位取反。
对11111111按位取反之后就是00000000,即十进制下的0,此时不会进入循环。
int a, b;
while(~scanf("%d%d", &a, &b)){
printf("%d\n", a+b);
}
C++版:
1.使用cin在遇到EOF的时候就会终止循环。
int a, b;
while(cin >> a >> b){
cout << a+b << endl;
}
1002:A+B问题,确定数据的组数
处理方法:输入组数T,循环处理每组数据
C语言版:
1.一般都使用T或者t来表示组数,while(T--)表示循环T次,每次T的值都会减少1,当T的值为0的时候退出循环。
int T;
scanf("%d", &T);
while(T--){
int a, b;
scanf("%d%d", &a, &b);
printf("%d\n", a+b);
}
C++版:
int T;
cin >> T;
while(T--){
int a, b;
cin >> a >> b;
cout << a+b << endl;
}
1003:A+B问题,不确定数据组数,但是有终止标志
处理方法:类似1001处理到文件末尾EOF实现循环读入,同时遇到终止标志时退出循环
C语言版:
1.读入之后检查a和b的值是否同时为0,是则退出,否则处理。
int a, b;
while(~scanf("%d%d", &a, &b)){
if(a == 0 && b == 0) break;
printf("%d\n", a+b);
}
2.在while语句中完成判断,表示没有读到EOF并且a、b的值不同时为0。
int a, b;
while(~scanf("%d%d", &a, &b) && (a != 0 || b != 0)){
printf("%d\n", a+b);
}
3.在while语句中判断a+b的值是否为0,a+b=0当且仅当a和b同时为0(若a、b均为非负数)。
int a, b;
while(~scanf("%d%d", &a, &b), a+b){ // 在这里","和"&&"等价
printf("%d\n", a+b);
}
C++版:
int a, b;
while(cin >> a >> b){
if(a == 0 && b == 0) break; // if(a+b) break;
cout << a+b << endl;
}
int a, b;
while(cin >> a >> b, (a != 0 || b != 0)){
cout << a+b << endl;
}
int a, b;
while(cin >> a >> b, a+b){
cout << a+b << endl;
}
1004:求和问题,不确定数据组数,但有终止标志
处理方法:与1003类似。
C语言版:
int n;
while(~scanf("%d", &n)){
if(n == 0) break; // if(!n) break; !符号表示取反,数字0变1,1变0,与~按位取反不同
int sum = 0;
for(int i = 0; i < n; i++){
int v;
scanf("%d", &v);
sum += v;
}
printf("%d\n", sum);
}
int n;
while(~scanf("%d", &n), n){
int sum = 0;
for(int i = 0; i < n; i++){
int v;
scanf("%d", &v);
sum += v;
}
printf("%d\n", sum);
}
C++版:
int n;
while(cin >> n){
if(!n) break;
int sum = 0;
for(int i = 0; i < n; i++){
int v;
cin >> v;
sum += v;
}
cout << sum << endl;
}
int n;
while(cin >> n, n){
int sum = 0;
for(int i = 0; i < n; i++){
int v;
cin >> v;
sum += v;
}
cout << sum << endl;
}
1005:求和问题,确定数据组数
处理方法:与1002类似。
C语言版:
int T;
scanf("%d", &T);
while(T--){
int n;
scanf("%d", &n);
int sum = 0;
for(int i = 0; i < n; i++){
int v;
scanf("%d", &v);
sum += v;
}
printf("%d\n", sum);
}
C++版:
int T;
cin >> T;
while(T--){
int n;
cin >> n;
int sum = 0;
for(int i = 0; i < n; i++){
int v;
cin >> v;
sum += v;
}
cout << sum << endl;
}
1006:求和问题,不确定数据组数
处理方法:与1001类似。
C语言版:
int n;
while(scanf("%d", &n) == 1){
int sum = 0;
for(int i = 0; i < n; i++){
int v;
scanf("%d", &v);
sum += v;
}
printf("%d\n", sum);
}
int n;
while(~scanf("%d", &n)){
int sum = 0;
for(int i = 0; i < n; i++){
int v;
scanf("%d", &v);
sum += v;
}
printf("%d\n", sum);
}
C++版:
int n;
while(cin >> n){
int sum = 0;
for(int i = 0; i < n; i++){
int v;
cin >> v;
sum += v;
}
cout << sum << endl;
}
1007:A+B问题,不确定数据组数,每个输出后有一个空行
处理方法:类似1001,每个输出多加一个换行符。
C语言版:
int a, b;
while(~scanf("%d%d", &a, &b)){
printf("%d\n\n", a+b); // 多一个\n
}
C++版:
int a, b;
while(cin >> a >> b){
cout << a+b << endl;
cout << endl; // 多一个endl
}
1008:求和问题,确定数据组数,每个输出之间有一个空行
处理方法:类似1002,除了最后一个输出之外每个输出多加一个换行符。
C语言版:
1.注意最后一行,在处理最后一个输出的时候T的值为0,故不输出空行。
int T;
scanf("%d", &T);
while(T--){
int n;
scanf("%d", &n);
int sum = 0;
for(int i = 0; i < n; i++){
int v;
scanf("%d", &v);
sum += v;
}
printf("%d\n", sum);
if(T) printf("\n");
}
C++版:
int T;
cin >> T;
while(T--){
int n;
cin >> n;
int sum = 0;
for(int i = 0; i < n; i++){
int v;
cin >> v;
sum += v;
}
cout << sum << endl;
if(T) cout << endl;
}
总结:若确定数据组数,使用T来控制;若没有确定数据组数,根据EOF来控制,有终止标志另外判断。
注:输出完一行之后不要忘了加上换行符!C是’\n’,C++是endl。
接下来就用C++来演示了,C语言类似。
二、1009和1012:高精的使用
1009:A+B问题,但是A、B非常大,可能有1000位,只能用字符串来保存
处理方法:使用高精加法。
代码:
const int L = 2000; // L表示字符串的长度
string add(string a,string b) //只限两个非负整数相加,只要会用就可以
{
string ans;
int na[L]={0},nb[L]={0};
int la=a.size(),lb=b.size();
for(int i=0;i<la;i++) na[la-1-i]=a[i]-'0';
for(int i=0;i<lb;i++) nb[lb-1-i]=b[i]-'0';
int lmax=la>lb?la:lb;
for(int i=0;i<lmax;i++) na[i]+=nb[i],na[i+1]+=na[i]/10,na[i]%=10;
if(na[lmax]) lmax++;
for(int i=lmax-1;i>=0;i--) ans+=na[i]+'0';
return ans;
}
int main()
{
int T;
cin >> T;
int kase = 1; // 用来控制Case后表示的数字
while(T--){
string a, b;
cin >> a >> b;
cout << "Case " << kase++ << ":" << endl;
cout << a << " + " << b << " = " << add(a, b) << endl;
if(T) cout << endl;
}
return 0;
}
1012:求N的阶乘N!,N非常大,求出的阶乘只能用字符串表示
处理方法:使用高精阶乘。
代码:
const int L = 100005;
int a[L];
string fac(int n) // 高精阶乘,只要会用就可以
{
string ans;
if(n==0) return "1";
fill(a,a+L,0);
int s=0,m=n;
while(m) a[++s]=m%10,m/=10;
for(int i=n-1;i>=2;i--)
{
int w=0;
for(int j=1;j<=s;j++) a[j]=a[j]*i+w,w=a[j]/10,a[j]=a[j]%10;
while(w) a[++s]=w%10,w/=10;
}
while(!a[s]) s--;
while(s>=1) ans+=a[s--]+'0';
return ans;
}
int main()
{
int n;
while(cin >> n){
cout << fac(n) << endl;
}
return 0;
}
三、1010:最大连续区间和(经典dp)
1010:求最大连续区间和,根据N的范围,1<=N<=1e5(即100000),要使用O(n)线性复杂度的算法。
注:测评机在1s的时间内一般只能处理1e8~1e9条语句。
分析:肯定不能使用暴力的做法,即枚举区间起点与终点,计算该区间的区间和,更新答案,这样的时间复杂度是O(n3),严重超时。而这种做法中可以将计算区间和优化一下,但是最少还是要O(n2),依然超时。
接下来我们考虑区间的贡献问题,先给出例子序列 0 6 -1 -7 5 2 -9。
从前往后计算当前的区间和:now表示当前的序列值,sum表示当前的区间和,ans表示答案。
① now: 0 sum: 0 ans: 0 [1,1]的区间和为0,对后续的答案有正向贡献,保留,并且更新ans为0
② now: 6 sum: 6 ans: 6 [1,2]的区间和为6,对后续的答案有正向贡献,保留,并且更新ans为6
③ now: -1 sum: 5 ans: 6 [1,3]的区间和为5,对后续的答案有正向贡献,保留
④ now: -7 sum: 0 ans: 6 [1,4]的区间和为-2,对后续答案没有贡献,放弃[1,4]区间,置sum为0
⑤ now: 5 sum: 5 ans: 6 [5,5]的区间和为5,对后续答案有正向贡献,保留
⑥ now: 2 sum: 7 ans: 7 [5,6]的区间和为7,对后续答案有正向贡献,保留,并且更新ans为7
⑦ now: -9 sum: 0 ans: 7 [5,7]的区间和为-2,对后续答案没有贡献,放弃[5,7]区间,置sum为0
我们可以发现从前往后遍历的时候只需要保留对后续答案有正向贡献的区间,并且随时更新ans即可。
代码:
int main()
{
int T;
cin >> T;
int kase = 1;
while(T--){
int n;
cin >> n;
int sum = 0;
int ans = -10000; // 初始化ans的值为最小值
// st表示最大区间左端点,ed表示最大区间右端点
// sst表示当前区间左端点,eed表示当前区间右端点
int st, ed, sst = 1, eed = 1;
for(int i = 1; i <= n; i++){
eed = i;
int v;
cin >> v;
sum += v;
if(sum > ans){ // 随时更新答案ans
st = sst;
ed = eed;
ans = sum;
}
if(sum < 0){ // 对后续没有正向贡献时丢弃,并且置sum为0
sst = i+1;
sum = 0;
}
}
cout << "Case " << kase++ << ":" << endl;
cout << ans << " " << st << " " << ed << endl;
if(T) cout << endl;
}
return 0;
}
四、1011:规律题
1011:f(1) = 1, f(2) = 1, f(n) = (A*f(n-1) + B*f(n-2))%7. 给定A、B以及n,求f(n)的值,1<=n<=1e8。
分析:根据递推式以及n的数据范围大体上可以判断这道题目一定有规律,题目不确定数据的组数并且每组数据的n最大可能是1e8,那么一般的算法基本上都顶不住,故考虑找规律。
注意到f(n)的值需要%7,因此f(n)的取值只有0~6这7个值,故A*f(n-1)和B*f(n-2)分别只有7个取值。
那么A*f(n-1)和B*f(n-2)最多只有7*7=49种组合方式。
因此在50为长度的周期之内至少会出现一对重复的A*f(n-1)、B*f(n-2),即重复的f(n-1)、f(n-2)对。
因此只需要处理49长度的f(n)值就可以求解。
代码:
const int MAXN = 50+10;
int arr[MAXN];
int main()
{
int A, B, n;
while(cin >> A >> B >> n, A+B+n){
arr[1] = arr[2] = 1;
for(int i = 3; i < 50; i++){
arr[i] = (A*arr[i-1]+B*arr[i-2])%7;
}
cout << arr[n%49==0?49:n%49] << endl;
}
return 0;
}
【END】感谢观看!