10月10日模拟题解题报告
~~今天高一秋游,我不想去(貌似我还交了钱~可惜了~),于是待(呆)在机房和高二大佬一起考试。。~~
1、统计单词数
(stat.cpp/c/pas)
一般的文本编辑器都有查找单词的功能,该功能可以快速定位特定单词在文章中的位
置,有的还能统计出特定单词在文章中出现的次数。
现在,请你编程实现这一功能,具体要求是:给定一个单词,请你输出它在给定的文章中出现的次数和第一次出现的位置。注意:匹配单词时,不区分大小写,但要求完全匹配,即给定单词必须与文章中的某一独立单词在不区分大小写的情况下完全相同(参见样例 1),如果给定单词仅是文章中某一单词的一部分则不算匹配(参见样例 2)。
【输入】
输入文件名为 stat.in,2 行。
第 1 行为一个字符串,其中只含字母,表示给定单词;
第 2 行为一个字符串,其中只可能包含字母和空格,表示给定的文章。
【输出】
输出文件名为 stat.out。 只有一行,如果在文章中找到给定单词则输出两个整数,两个整数之间用一个空格隔开,
分别是单词在文章中出现的次数和第一次出现的位置(即在文章中第一次出现时,单词首字母在文章中的位置,位置从 0 开始);如果单词在文章中没有出现,则直接输出一个整数-1。
【输入输出样例 1】
stat.in
To
to be or not to be is a question
stat.out
2 0
【输入输出样例 1 说明】
输出结果表示给定的单词 To 在文章中出现两次,第一次出现的位置为 0。
【输入输出样例 2】
Stat.in
to
Did the Ottoman Empire lose its power at that time
stat.out
-1
【输入输出样例 2 说明】
表示给定的单词 to 在文章中没有出现,输出整数-1。
【数据范围】
1 ≤ 单词长度 ≤ 10。
1 ≤ 文章长度 ≤ 1,000,000。
Solution:
1、首先这是某年普及组的水(难)题。
2、昨天才被第一题坑过,所以,呵呵呵~~,果然单词有多个空格。
3、思路直接模拟,先把字母大小写统一(本题不区分大小写,所以要统一),然后枚举每一个文章中的单词,与原本的单词进行比较。
枚举的过程:枚举每一个文章中的字母,然后从当前字母 A 开始向后枚举所需求单词 B 的长度,然后 A 与 B 比较,如果完全相同,然后再判断单词 A 的前、后是否都是空格(否则有可能出现枚举一个单词一部分的情况),如果是第一次更新,就把答案设置为枚举时所循环的值(一般是 i)。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #include<string> 7 using namespace std; 8 int main() 9 { 10 freopen("stat.in","r",stdin); 11 freopen("stat.out","w",stdout); 12 string a,b; 13 int x=-1,y=0; 14 getline(cin,a);getline(cin,b); 15 int la=a.length(),lb=b.length(); 16 int i=0; 17 while(i<lb){ 18 while(b[i]==' '&&i<lb)i++; 19 int j=0; 20 while(j<la&&i<lb){ 21 if(a[j]==b[i]||abs(a[j]-b[i])==32){i++;j++;} 22 else 23 break;} 24 if(j==la&&(b[i]==' '||i==lb-1)) 25 { 26 if(x==-1)x=i-la; 27 y++; 28 } 29 while(b[i]!=' '&&i<lb)i++; 30 } 31 if(x==-1)cout<<x<<endl; 32 else cout<<y<<' '<<x<<endl; 33 return 0; 34 }
2、滑雪
ski.cpp/in/out
1s / 128M
【问题描述】
滑雪是一项非常刺激的运动,为了获得速度,滑雪的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。给出一个由二维数组表示的滑雪区域,数组的数字代表各点的高度。请你找出这个区域中最长的滑坡。
下面是一个例子:
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可滑行的滑坡为24-17-16-1。当然,25-24-23-...-3-2-1更长。事实上,这是最长的一条滑坡。
【输入文件】
输入文件ski.in的第一行为两个数R, C,表示滑雪区域的行数和列数(1≤R,C≤100)。下面是R行,每行有C个整数,表示高度H(0≤H≤10000)。
【输出文件】
输出文件ski.out包括一行,只包含一个整数,表示滑雪区域中最长滑坡的长度,即经过的点的数量最大值。
【样例输入】
5 5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
【样例输出】
25
Solution:
1、很熟悉的题,没错《C++一本通》(C++从入门到入土~)上面的题,当时听得不太懂…现在回过头来看,哦呵呵太easy了。
2、记忆化搜索F[i][j]表示走到 i 行 j 列的最大长度枚举从每一个点出发 f[i][j] = max(f[i][j],f[i + dx[k]][j + dy[k]] + 1) ,dx,dy 是方向数组。图上不好递推,则采用搜索的形式,一定要记忆化。(不懂就翻书吧,上面写的比我详细)
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cmath> 5 #include<cstring> 6 #include<cstdlib> 7 #include<string> 8 #include<queue> 9 #include<map> 10 using namespace std; 11 int dx[5]={0,-1,0,1,0},dy[5]={0,0,1,0,-1}; 12 int r,c,p,t,ans,m[105][105],f[105][105]; 13 inline int dfs(int x,int y) 14 { 15 int t,tmp,nx,ny; 16 if(f[x][y])return f[x][y]; 17 t=1; 18 for(int i=1;i<=4;i++) 19 { 20 nx=x+dx[i];ny=y+dy[i]; 21 if(nx>=1&&ny<=r&&ny>=1&&ny<=c&&m[x][y]<m[nx][ny]) 22 { 23 tmp=dfs(nx,ny)+1; 24 if(tmp>t)t=tmp; 25 } 26 } 27 f[x][y]=t; 28 return t; 29 } 30 int main() 31 { 32 freopen("ski.in","r",stdin); 33 freopen("ski.out","w",stdout); 34 scanf("%d%d",&r,&c); 35 for(int i=1;i<=r;i++) 36 for(int j=1;j<=c;j++)scanf("%d",&m[i][j]); 37 for(int i=1;i<=r;i++) 38 for(int j=1;j<=c;j++) 39 { 40 t=dfs(i,j); 41 f[i][j]=t; 42 if(t>ans)ans=t; 43 } 44 printf("%d",ans); 45 return 0; 46 return 0; 47 }
3、公司控制
control.cpp/in/out
1s / 128M
【题目描述】
有些公司是其他公司的部分拥有者,因为他们获得了其他公司发行的股票的一部分。例如,福特公司拥有马自达公司12%的股票。据说,如果至少满足了以下三个条件之一,公司A就可以控制公司B了:
1、公司A = 公司B。
2、公司A拥有大于50%的公司B的股票。
3、公司A控制K(K >= 1)个公司,记为C1, ..., CK,每个公司Ci拥有xi%的公司B的股票,并且x1+ .... + xK > 50%。(包括公司本来持有的股票)
给你一个表,每行包括三个数(i,j,p);表明公司i享有公司j的p%的股票。计算所有的数对(h,s),表明公司h控制公司s。至多有100个公司。
写一个程序读入N组数(i,j,p),i,j和p是都在范围(1..100)的正整数,并且找出所有的数对(h,s),使得公司h控制公司s。
【输入数据】
输入文件名:control.in
第一行: N,表明接下来三个数的数量,即(i,j,p)的数量。
第二行到第N+1行: 每行三个整数作为一个三对数(i,j,p),表示i公司拥有j公司 p%的股份。
【输出数据】
输出文件名:control.out
输出零个或更多个的控制其他公司的公司。每行包括两个整数A、B,表示A公司控制了B公司。将输出的数对以升序排列。注意,自己控制自己的不用输出。
【输入样例】
3
1 2 80
2 3 80
3 1 20
【输出样例】
1 2
1 3
2 3
【数据范围】
对于100%的数据,N<=5000,1<i,j,p<=100
Solution:
1、这道题其实很容易,因为数据很水,才100个公司,所以随便搜索一下就好了。考试的时候想到了搜索,不知怎么的灵机一动(脑子一抽)写了个floyd,结果呵呵呵~~70分,三个点答案错误。。仔细一想,我的方法对于第三种情况判断后没有再进行第二种情况的判断,于是改了多次判断,结果成了90分,还是wa一个点。。再想,点的重复啊什么反例啊什么各种情况(于是烦~懒得写),果断打搜索。搜索其实很短的,气死哦我了,搞了半天还是写了搜索。。
2、设 a[i][j]为 i 公司控制 j 公司的股份,con[i][j]表示 i 公司是否控制 j 公司。首先枚举每个 i,j 公司,对于每个 i 能控制的公司 j,则要继续枚举 j 占有股份的公司 k,计算其股份和,跟新 a[i][k],然后添加 i 控制 j,而每次添加新的控制关系则要再更新一次所有关系。时间复杂度小于 O(n^4),对于本题完全能过。
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cmath> 5 #include<cstdlib> 6 #include<vector> 7 #include<set> 8 #include<map> 9 #include<queue> 10 #include<cstring> 11 #include<string> 12 using namespace std; 13 int n,m,dis[101][101]; 14 bool vis[101][101]; 15 inline void pd(int i,int k){ 16 if(vis[i][k])return; 17 vis[i][k]=1; 18 for(int j=1;j<=n;j++){ 19 dis[i][j]+=dis[k][j]; 20 if(dis[i][j]>50)pd(i,j); 21 } 22 } 23 int main() 24 { 25 freopen("control.in","r",stdin); 26 freopen("control.out","w",stdout); 27 scanf("%d",&m); 28 //memset(dis,0x3f,sizeof(dis)); 29 for(int i=1;i<=m;i++) 30 { 31 int x,y,z; 32 scanf("%d%d%d",&x,&y,&z); 33 dis[x][y]=z;n=max(n,max(x,y)); 34 } 35 for(int i=1;i<=n;i++) 36 for(int j=1;j<=n;j++) 37 if(dis[i][j]>50)pd(i,j); 38 for(int i=1;i<=n;i++) 39 for(int j=1;j<=n;j++) 40 if(vis[i][j]&&i!=j)printf("%d %d\n",i,j); 41 return 0; 42 }
4、圆圈
circle.cpp/in/out
1s / 128M
[题目描述]
现在有一个圆圈,顺时针标号分别从0到n-1,每次等概率顺时针走一步或者逆时针走一步,即如果你在i号点,你有1/2概率走到((i-1)mod n)号点,1/2概率走到((i+1)mod n)号点。问从0号点走到x号点的期望步数。
[输入]
第一行包含一个整数T,表示数据的组数。
接下来有T行,每行包含两个整数n, x。
T<=10, 0<=x<n<=300;
[输出]
对于每组数据,输出一行,包含一个四位小数表示答案。
[样例输入]
3
3 2
5 4
10 5
[样例输出]
2.0000
4.0000
25.0000
[数据范围]
对于 30% 的数据,n<=20
对于 50% 的数据,n<=100
对于 70% 的数据,n<=200
对于 100%的数据,n<=300
Solution:
1、这题贼有意思,开始考虑dp,但是当往邻点走了过后可能又会走回来,绕过来绕去,很难处理dp的后效性问题。。于是考虑找规律(立flag),找了半天,怕是错的不敢打。。再去想拿个暴力分,搜索,死循环,f**k,不屑(写)了。。之后看了看最后一题,什么字符字串啊,数据还200000,果断放弃,于是去打乒乓球去了~。
2、考完后,看了一位神犇--撸哥的代码,顿时惊呆了(打一个dp暴力然后找规律,五行代码AC),flag:早知道我也去找规律了。。下面是找规律的代码:
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 int t,n,x; 5 int main() 6 { 7 freopen("circle.in","r",stdin); 8 freopen("circle.out","w",stdout); 9 scanf("%d",&t); 10 while(t--) 11 { 12 scanf("%d%d",&n,&x); 13 printf("%d.0000\n",(n-x)*x); 14 } 15 return 0; 16 }
3、正解是高斯消元。
期望公式:
E[x]=0 (x==0);
E[x]=0.5*(E[x-1]+1)+0.5*(E[x+1]+1); (x!=0)
移项得,-E[i-1]*0.5+E[i]*0.5-E[i+1]*0.5=1
n个方程高斯消元求解。因为每个方程只有三个不为零系数,所以当TLE可以适当剪枝。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <string.h> 5 #include <cmath> 6 #include <iomanip> 7 #include <algorithm> 8 using namespace std; 9 10 ///浮点型高斯消元模板 11 const double eps=1e-12; 12 const int maxm=1000;///m个方程,n个变量 13 const int maxn=1000; 14 int m,n; 15 double a[maxm][maxn+1];///增广矩阵 16 bool free_x[maxn];///判断是否是不确定的变元 17 double x[maxn];///解集 18 19 int sign(double x) 20 { 21 return (x>eps)-(x<-eps); 22 } 23 /**返回值: 24 -1 无解 25 0 有且仅有一个解 26 >=1 有多个解,根据free_x判断哪些是不确定的解 27 */ 28 int Gauss() 29 { 30 int i,j; 31 int row,col,max_r; 32 m=n;///n个方程,n个变量的那种情况 33 for(row=0,col=0;row<m&&col<n;row++,col++) 34 { 35 max_r=row; 36 for(i=row+1;i<m;i++)///找到当前列所有行中的最大值(做除法时减小误差) 37 { 38 if(sign(fabs(a[i][col])-fabs(a[max_r][col]))>0) 39 max_r=i; 40 } 41 if(max_r!=row) 42 { 43 for(j=row;j<n+1;j++) 44 swap(a[max_r][j],a[row][j]); 45 } 46 if(sign(a[row][col])==0)///当前列row行以下全为0(包括row行) 47 { 48 row--; 49 continue; 50 } 51 for(i=row+1;i<m;i++) 52 { 53 if(sign(a[i][col])==0) 54 continue; 55 double tmp=a[i][col]/a[row][col]; 56 for(j=col;j<n+1;j++) 57 a[i][j]-=a[row][j]*tmp; 58 } 59 } 60 for(i=row;i<m;i++)///col=n存在0...0,a的情况,无解 61 { 62 if(sign(a[i][col])) 63 return -1; 64 } 65 if(row<n)///存在0...0,0的情况,有多个解,自由变元个数为n-row个 66 { 67 for(i=row-1;i>=0;i--) 68 { 69 int free_num=0;///自由变元的个数 70 int free_index;///自由变元的序号 71 for(j=0;j<n;j++) 72 { 73 if(sign(a[i][j])!=0&&free_x[j]) 74 free_num++,free_index=j; 75 } 76 if(free_num>1) 77 continue;///该行中的不确定的变元的个数超过1个,无法求解,它们仍然为不确定的变元 78 ///只有一个不确定的变元free_index,可以求解出该变元,且该变元是确定的 79 double tmp=a[i][n]; 80 for(j=0;j<n;j++) 81 { 82 if(sign(a[i][j])!=0&&j!=free_index) 83 tmp-=a[i][j]*x[j]; 84 } 85 x[free_index]=tmp/a[i][free_index]; 86 free_x[free_index]=false; 87 } 88 return n-row; 89 } 90 ///有且仅有一个解,严格的上三角矩阵(n==m) 91 for(i=n-1;i>=0;i--) 92 { 93 double tmp=a[i][n]; 94 for(j=i+1;j<n;j++) 95 if(sign(a[i][j])!=0) 96 tmp-=a[i][j]*x[j]; 97 x[i]=tmp/a[i][i]; 98 } 99 return 0; 100 }///模板结束 101 102 int t,xx; 103 104 int main() 105 { 106 freopen("circle.in","r",stdin); 107 freopen("circle.out","w",stdout); 108 cin>>t; 109 while(t--) 110 { 111 cin>>n>>xx; 112 memset(a,0,sizeof(a)); 113 for(int i=0;i<n;i++) 114 { 115 if(i==xx) 116 { 117 a[i][i]=1; 118 a[i][n]=0; 119 continue; 120 } 121 a[i][i]=1; 122 a[i][n]=1; 123 a[i][(i-1+n)%n]=-0.5; 124 a[i][(i+1)%n]=-0.5; 125 } 126 Gauss(); 127 cout<<setiosflags(ios::fixed)<<setprecision(4)<<x[0]<<endl; 128 } 129 return 0; 130 }
5、字符串
string.cpp/in/out
4s / 128M
[题目描述]
给一个字符串,在所有不同的子串中,找到字典序第K大的子串。
如abab的所有不同子串有(已按字典序排列)
a
ab
aba
abab
b
ba
bab
[输入]
第一行包含一个字符串S,只包含小写字母。
第二行包含一个整数Q,表示询问的个数。
接下来有Q行,每行包含一个整数K,表示要查询第K大的子串。
|S|<=90000, Q<=500, 0<K<2^31,并且K不超过S不同子串的个数。
[输出]
对于每个询问,输出一行包含字典序第K大的子串。
[样例输入]
aaa
2
2
3
[样例输出]
aa
aaa
[数据范围]
对于 20% 的数据,|S|<=200
对于 50% 的数据,|S|<=5000
对于 80% 的数据,|S|<=50000
对于 100%的数据,|S|<=90000
Solution:
1、这题我直接放弃了,暴力是没分的,等我学了AC自动机再回过头解析这题(立FALG)。
2、这里是李总给的标程和解析:题目来源 SPOJ SUBLEX 目标是省队的同学自己去搜题解吧。没错,就这么点完了~~
于是我搜了一下网上的解析也很少:
首先对于给出的字符串建立后缀自动机, 然后利用后缀自动机的性质, 所有相同的子串一定会在同一点终止, 那么, 从根开始, 每次都选择尽量小的字符走, 首先我们可以dfs预处理出每个状态点处代表的可能向下的不同字串有多少个, 然后就知道每个字符向下走会有多少种可能了, 于是根据这个图中每次沿着满足向下能找到第K小的边走即可。
1 #include <cstdio> 2 #include <cstring> 3 #include <queue> 4 #include <vector> 5 6 using namespace std; 7 8 const int maxn = 90000; 9 10 int last, cnt, root; 11 char s[maxn + 1]; 12 int ch[maxn * 2][26]; 13 int mx[maxn * 2]; 14 int fa[maxn * 2]; 15 int deg[maxn * 2]; 16 vector<int> G[maxn * 2]; 17 long long sz[maxn * 2]; 18 19 void insert(int x) 20 { 21 int np = ++cnt; 22 int p = last; 23 mx[np] = mx[p] + 1; 24 while (p && !ch[p][x]) 25 ch[p][x] = np, p = fa[p]; 26 if (!p) 27 fa[np] = root; 28 else 29 { 30 int q = ch[p][x]; 31 if (mx[q] == mx[p] + 1) 32 fa[np] = q; 33 else 34 { 35 int nq = ++cnt; 36 mx[nq] = mx[p] + 1; 37 for (int j = 0; j < 26; ++j) 38 ch[nq][j] = ch[q][j]; 39 fa[nq] = fa[q]; 40 fa[np] = fa[q] = nq; 41 while (p && ch[p][x] == q) 42 ch[p][x] = nq, p = fa[p]; 43 } 44 } 45 last = np; 46 } 47 48 int main() 49 { 50 freopen("string.in","r",stdin); 51 freopen("string.out","w",stdout); 52 scanf("%s", s); 53 int n = strlen(s); 54 root = last = ++cnt; 55 for (int i = 0; i < n; ++i) 56 insert(s[i] - 'a'); 57 for (int i = 1; i <= cnt; ++i) 58 for (int j = 0; j < 26; ++j) 59 if (ch[i][j]) 60 { 61 ++deg[i]; 62 G[ch[i][j]].push_back(i); 63 } 64 queue<int> q; 65 for (int i = 1; i <= cnt; ++i) 66 if (!deg[i]) 67 q.push(i); 68 while (!q.empty()) 69 { 70 int u = q.front(); 71 q.pop(); 72 ++sz[u]; 73 for (int i = 0; i < G[u].size(); ++i) 74 { 75 int v = G[u][i]; 76 sz[v] += sz[u]; 77 if (--deg[v] == 0) 78 q.push(v); 79 } 80 } 81 int Q; 82 scanf("%d", &Q); 83 while (Q--) 84 { 85 int K; 86 scanf("%d", &K); 87 int p = root; 88 while (K > 0) 89 { 90 int sum = 0; 91 for (int i = 0; i < 26; ++i) 92 if (sum + sz[ch[p][i]] >= K) 93 { 94 K -= sum + 1; 95 putchar('a' + i); 96 p = ch[p][i]; 97 break; 98 } 99 else 100 sum += sz[ch[p][i]]; 101 } 102 puts(""); 103 } 104 return 0; 105 }