第七届山东省省赛题解
Posted aiguona
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第七届山东省省赛题解相关的知识,希望对你有一定的参考价值。
A - Julyed
类型:
水题
题意:
Julyed正在为她的大学英语六级考试做准备。她有N个字要记,但只剩M天了。如果她记不住这些话,她就不会通过大学英语六级考试。如果她不能通过大学英语六级考试,她就会不高兴。但如果她在某一天记得太多的话,她也会不开心。如果她不高兴,汤姆就会不高兴。所以她会在一天之内记住尽可能少的单词。为了快乐和快乐,在一天中,最多将会有多少个单词会被记住呢?
题解:
水题 N / 天数 上取整
代码:
#include<cstdio> #include<iostream> #include<cmath> using namespace std; int main() { int T; cin >> T; while(T--) { int a,b; cin >> a >> b; int ans = ceil(a * 1.0/ b * 1.0); cout << ans << endl; } return 0; }
B - Fibonacci
题意:
给定一个整数N,判断N是否可以写成不连续的斐波那契数之和。若能输不能输出-1,若能输出 N = f1 + f2 + f3 + … + fn.
题解:
首先不存在-1的情况。齐肯多夫定理:任何正整数都可以唯一地表示成若干个不连续的斐波那契数之和。
证明:
数学归纳法,可以自行查找
那么只需要打好斐波那契表,逆序遍历Fib数,存到输出数组里,最后按照格式逆序输出即可
int Fib[50]; int ans[50]; void init() { Fib[1]=1,Fib[2]=2; for(int i=3;i<=43;i++) Fib[i]=Fib[i-1]+Fib[i-2]; } int main() { init(); int T; cin>>T; while(T--) { int n; cin>>n; int len=0,put=n; for(int i=43;i>=1;i--) if(n>=Fib[i]) ans[++len]=Fib[i],n-=Fib[i]; cout<<put<<"="<<ans[len]; for(int i=len-1;i>=1;i--) cout<<"+"<<ans[i]; cout<<endl; } }
C - Proxy
题意:
寻找给定起点和目标点的最短路上,与起点相连的编号最小的节点序号。类型:图论,思维
题解:
这是一道最短路问题。 要求有以下几点:
①求最短路,如果没有输出-1;
②输出最短路当中的距离起点最近的那个点
③存在多个最短路时,输出最小的那个点
第一点套模版即可。
第二点,我们记录路径往往最容易查询距离终点最近的那个点。所以我们可以建反向边,这样就可以查询距离起点最近得了。
第三点,这是这道题的核心考点。 如果某点加上当前的路径长度刚好等于最短路的长度,那么说明存在两条最短路了,这时候比较当前点和原本终点的前一个点的大小,选择小的替换
题型:
图论
代码:
int INF = 1<<29; int mp[1010][1010]; int n,m; int d[1010]; int path[1010]; bool vis[1010]; void init(int n) { for(int i=0; i<n; i++) for(int j=0; j<n; j++) { if(i==j) mp[i][j]=0; else mp[i][j]=INF; } } void dijkstra() { int i,j,minn,v; for(i=0; i<n; i++) { vis[i]=0; d[i]=mp[n-1][i]; if(mp[n-1][i]<INF) path[i]=0; else path[i]=-1; } path[n-1]=0; for(i=0; i<n; i++) { minn=INF; for(j=0; j<n; j++) if(!vis[j] && d[j]<minn) { v=j; minn=d[j]; } vis[v]=1; for(j=0; j<n; j++) if(!vis[j] && d[j]>mp[v][j]+d[v]) { d[j]=mp[v][j]+d[v]; path[j]=v; } else { if(!vis[j]&&d[j]==mp[v][j]+d[v]) { path[j]=min(path[j],v); } } } } int main() { int t,a,b,c; cin>>t; while(t--) { cin>>n>>m; n+=2; init(n); for(int i=0; i<m; i++) { cin>>a>>b>>c; mp[b][a]=c; } dijkstra(); if(d[0]==INF) { cout<<"-1"<<endl; } else { cout<<path[0]<<endl; } } }
D - Swiss-system tournament
题意:
给出2*n个人,每次第1个人和第2个人比赛,第3个人和第4个人比赛…进行r轮比赛,每轮比完赛都进行排名,求出最后第q名是谁。给出每个人的积分 s[i],每个人的能力a[i],能力大的获胜,获胜的加1分,输了的不加分。
类型:
思维 ,归并思想
题解:
这个题目用到了归并排序的思想,每轮比赛,把赢者放一组,输者放一组,这样单个数组也是有序的,然后进行合并。时间复杂度为0(2n*R)
代码:
#include <stdio.h> #include <algorithm> using namespace std; struct node { int a,b; int index; }; node ans[200005]; node temp1[200005]; node temp2[200005]; bool cmp(node a,node b) { if(a.a==b.a) return a.index<b.index; return a.a>b.a; } int main() { int T; int n,r,q; int i,j,k,l; scanf("%d",&T); while(T--) { scanf("%d %d %d",&n,&r,&q); for(i=0; i<2*n; i++) { scanf("%d",&ans[i].a); ans[i].index=i+1; } for(i=0; i<2*n; i++) scanf("%d",&ans[i].b); sort(ans,ans+(2*n),cmp); for(i=0; i<r; i++) { int cnt1=0,cnt2=0; for(j=0; j<2*n; j+=2) { if(ans[j].b > ans[j+1].b) { ans[j].a++; temp1[cnt1++] = ans[j]; temp2[cnt2++] = ans[j+1]; } else { ans[j+1].a++; temp1[cnt1++] = ans[j+1]; temp2[cnt2++] = ans[j]; } } j=0; k=0; l=0; while(j<cnt1 && k<cnt2) { if(temp1[j].a == temp2[k].a) { if(temp1[j].index < temp2[k].index) { ans[l++] = temp1[j]; j++; } else { ans[l++] = temp2[k]; k++; } } else if(temp1[j].a > temp2[k].a) { ans[l++] = temp1[j]; j++; } else { ans[l++] = temp2[k]; k++; } } while(j<cnt1) ans[l++] = temp1[j++]; while(k<cnt2) ans[l++] = temp2[k++]; } printf("%d ",ans[q-1].index); } return 0; }
E - The Binding of Isaac
题意:
就是找有多少个位置满足只与‘#’有且仅有一条公共边
题解:
暴力,数据量100 坑点 就是所给区域的外围的区域也算,翻译的时候会看到。
类型:
搜索,不过数据量比较水,是个签到,不过背景很新。
代码:
#include<stdio.h> int main() { char map[105][105]; int i,j,n,m,l,sum,k; scanf("%d",&l); while(l--) { k=0; sum=0; scanf("%d%d",&m,&n); getchar(); for(i=0;i<105;i++) for(j=0;j<105;j++) map[i][j]=0; for(i=1;i<=m;i++) { for(j=1;j<=n;j++) scanf("%c",&map[i][j]); getchar(); } for(i=1;i<=m;i++) for(j=1;j<=n;j++) { if(map[i][j]==‘#‘) { if(map[i+1][j]==0) sum++; if(map[i-1][j]==0) sum++; if(map[i][j+1]==0) sum++; if(map[i][j-1]==0) sum++; } if(map[i][j]==‘.‘) { if(map[i+1][j]==‘#‘) k++; if(map[i-1][j]==‘#‘) k++; if(map[i][j+1]==‘#‘) k++; if(map[i][j-1]==‘#‘) k++; if(k==1) sum++; k=0; } } printf("%d ",sum); } return 0; }
F - Feed the monkey
真~不会
G - Triple Nim
题意:
有一堆石子,一共有n个,把n个石子分成三堆,求有多少种分配的方式能够使得bob win?
题解:
算是两个题解吧。
第一:非正式的,就是打表找规律。
规律如下:
如果 n 是 奇数,直接输出 0.
如果是偶数,并且是 2 的某个次方,输出 0.
否则统计二进制中 1 的个数。
两个 1 答案为 1
三个 1 答案为 4
四个 1 答案为 13
五个 1 答案为 40
六个 1 答案为 121
可以把答案单独开个数组 F[ N ] = F [ N -1 ] * 3 +1;
第二:正式的,这就是个Nim博弈
尼姆博弈是利用二进制的思想,那么本题也可以利用二进制的思想,可知,如果要使得Alice输并且Alice为先手,只需要使得三堆石子异或等于0 即可,首先共有n个石子,把n转化成二进制来表示,假设其中有k个1存在,如果要使得三堆石子异或为0,则在三堆石子数的二进制数的每位上1的个数都要是偶数位,又可知,在二进制中,高位的1是由低位进位而得到的,也就是说高位的1可以分解成两个低位的1,当n是奇数的时候,最低位为1且没有办法分解,所以输出0,所以当n为偶数的时候,就有(3^k - 3)/6个,减去3是去掉一个为0的情况,除6是应为本题求得是排列。
题型:
博弈
代码1:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; long long int f[50]; int main() { int T; cin>>T; f[1]=0; f[2]=1; for(int i=3;i<=49;i++) f[i]=f[i-1]*3+1; while(T--) { long long int n; cin>>n; if(n%2) { cout<<0<<endl; } else { long long int x=n; int num=0; while(x) { if(x%2) num++; x/=2; } cout<<f[num]<<endl; } } }
代码2:
#include <bits/stdc++.h> using namespace std; int main() { int t; scanf("%d",&t); while(t--) { long long n; cin>>n; if(n%2)cout<<"0"<<endl; else { int num=0; while(n) { if(n%2)num++; n=n/2; } long long ans=(pow(3,num)-3)/6; cout<<ans<<endl; } } return 0; }
H - Memory Leak
题意:
内存泄漏是C/ c++中一个众所周知的bug。当一个字符串比预期的长时,它将访问下一个数组的内存,这将导致问题并泄漏一些信息。你可以看到一个简单的例子:
如我们所见,如果输入字符串的长度等于或大于数组的极限长度,则额外的部分将不会被存储,下一个数组的信息将在输出时被泄漏。输出将停止,直到找到“ ”字符(字符串末尾的符号)。在这个问题中,程序永远不会有意外的结束,最后一个数组不会泄漏其他信息。
提供的源代码如下:
INPUT
多个测试用例,第一行是整数T (T <= 20),表示测试用例的数量。
每个测试用例的第一行包含一个非空字符串,即字符串的定义,格式化为“char s1[s1_len], s2[s2_len]…”。“char”是数组的类型,它永远不会改变。s1,s2……是数组的名称。s1_len s2_len…长度限制。如果没有出错,数组应该能够存储输入字符串和“ ”。不同数组的定义将用逗号和空格分开。长度限制为正,长度限制之和小于10000。
然后,将会有几行字符串,其中包含两到三个部分。
第一部分是“get”或“cout”,第二部分是字符串s,表示数组的名称。s将只包含小写字母和数字数字,并以字母开头。在一个例子中s是不同的。如果第一部分是“get”,那么将会有第三部分,一个字符串,该字符串应该被输入到数组s中,输入字符串的长度将小于1000,并且只包含可见的ASCII字符。“get”操作将重写数组,无论之前数组中是什么,然后在字符串后面添加“ ”。不同的部分被一个空间隔开。
Case以“return 0”结尾;
整个输入文件小于10MB。
对于每个“cout”,您应该输出一行包含实际输出的内容,这意味着您应该考虑内存泄漏的问题。如果请求的数组为空,则应该输出空行。
类型:
大模拟题 ,字符串处理。还有对英语也是一种考验,很容易漏条件。
题解:
判每个数组的类型和长度,然后就检查有没有内存泄漏。
代码:
#include <bits/stdc++.h> using namespace std; struct node { int l,r; }e[11111]; int top; char s[11111]; char c[11111][1111]; char op[11111]; int main() { int T; scanf("%d",&T); while(T--) { memset(s,0,sizeof(s)); int pos = 0; top = 0; scanf("%s",op); while(1) { scanf("%s",op); int len = strlen(op); int num = 0,i = 0; for(i = 0;i < len; i++) { if(op[i] != ‘[‘) c[top][i] = op[i]; else break; } c[top][i] = ‘