SWPU信息学院团体程序设计竞赛题解
A 简单的字符串处理
给你两个字符串s1,s2,若s1中的一部分与字符串s2完全相同,则删去这部分。如果删除后s1中仍存在某一部分与s2相同,则继续删除下去,直到s1中没有任何一部分与s2相同
多组输入
第一行给你两个字符串,用空格分开,保证字符串s2中的各个字符都是不同的,字符串s1,s2长度大于等于1小于等于100
输出s1字符串,若s1字符串为空,则输出NULL
aaawww aw
awslawslaw awsl
qifei wuhu
NULL
aw
qifei
对于第一组样例,字符串s1的变化过程为
aaawww--->aaww--->aw--->NULL
对于第二组样例,字符串s1的变化过程为
awslawslaw--->aw
对于第三组样例,字符串s1的变化过程为
qifei
这是本场的第一道题,难度适中,主要考察字符串的处理问题。
解题思路:每次删除S1中的S2就把那些字符替换为'\0',然后每次循环把S1更新一遍,最后S1的长度为0输出NULL否则输出剩下的S1。
如果单次循环未删除任何字符就退出break。注意多组输入!!!
代码如下:
#include<bits/stdc++.h> using namespace std; int main(void) { char s1[105],s2[105],ch[105];//ch是中转字符串,用于更新S1 while(cin>>s1>>s2) { int lens1=strlen(s1); int lens2=strlen(s2); int len=lens1; while(1) { int flag=1;//判断是否有删除操作 lens1=strlen(s1); memset(ch,0,sizeof(ch)); for(int i=0;i<lens1;++i) ch[i]=s1[i]; for(int i=0;i<lens1;++i) { if(ch[i]==s2[0]) { int tag=0; for(int j=1;j<lens2;++j) { if(ch[i+j]!=s2[j]) { tag=1; break; } } if(!tag) { for(int j=0;j<lens2;++j) ch[i+j]='\0'; flag=0; len-=lens2; i+=lens2-1; } } } if(flag)//如果没有删除操作就跳出循环 break; memset(s1,0,sizeof(s1));//更新S1 for(int i=0,j=0;i<lens1;++i)//更新S1 { if(ch[i]!='\0') { s1[j++]=ch[i]; } } } if(!len)//S1长度为0 cout<<"NULL"<<endl; else { for(int i=0;i<lens1;++i) { cout<<s1[i]; } cout<<'\n'; } memset(s1,0,sizeof(s1));//清空串 memset(s2,0,sizeof(s2)); memset(ch,0,sizeof(ch)); } return 0; }
这道题仔细想下还是蛮简单的,细心一点很快就能AC,朴素判断下就能AC,没有卡时间,很良心。
B 多米诺骨牌
给定n个多米诺骨牌,其中缺少掉了k个,需要至少m个连续的,求最少需要补充多少块
第一行n,m,k(1≤k,m≤ n ≤ 100000
接下来k行,分别代表多米诺骨牌缺失的位置
最少需要补充几块
10 5 5
1
5
7
8
10
1
这道题在比赛的时候没能做出来是最大的遗憾,这是移到水题,我开始以为是动态规划,后来在学长的解释下发现这道题是模拟做法。
思路:用一个数组记录米诺骨牌的情况,如果缺少就为1,未缺少就为0,因为求的是m的连续牌,这就有点像滑动窗口算法了
用一个max_记录前m个元素中需要补的个数,再让res等于max_,最后再通过遍历,从m+1开始,每次变化的只有首尾两个元素,减去尾部加上首部,再与上一次的比较,选最小的赋值给res
代码如下:
#include <bits/stdc++.h> #include <queue> using namespace std; int n, m, k, max_, res,l; int dp[100005]; int main(void) { cin >> n >> m >> k; max_ = 0; memset(dp, 0, sizeof(dp));//清空数组 for (int i = 1; i <= k; ++i) { cin >> l; dp[l]++;//将需要补的地方+1 } for (int i = 1; i <= m; ++i)//遍历这个数组找出钱m个牌中需要补的数目 { max_ += dp[i]; res = max_; } for (int i = m + 1; i <= n; ++i)//遍历找出需要补的最少的数目 { max_ += dp[i] - dp[i - m]; res = min(max_, res);//不断的更新最小值 } cout << res << endl; }
B题难在怎么理解它,怎么才能使它最小
这道题还能用DP做,代码如下:
#include <bits/stdc++.h> using namespace std; int n, m, k, max_=0, res,l; int dp[100005]; int main(void) { cin >> n >> m >> k; for (int i = 1; i <= k; ++i) cin >> l,dp[l]++; for (int i=1;i<=n;++i) dp[i]+=dp[i-1]; res=dp[m]; for (int i = m + 1; i <= n; ++i) { max_ = dp[i] - dp[i - m]; res = min(max_, res); } cout << res << endl; }
也算是滑动窗口的变形吧
C 翻转棋
给定一个n*m的黑块地图,再给定k个点,
每落下点则将此点以及此点上下左右四个方向所有棋子翻转。(白转黑,黑转白)
求最后的黑地图块有几块
第一行n,m,k(1≤n,m≤100,1≤k≤n*m)
接下来k行,每行代表一个棋子落点
输出最后的黑色地图块
5 5 2
1 1
3 3
6
地图范围为(1,1)到(n,m)
这道题就是一个DFS搞定,类似油田问题,不过有个反转处理
解题思路:先写一个二维矩阵用于储存地图用0表示黑棋1表示白棋,然后每当读到反转棋子就直接行列遍历反转一边。最后再遍历记录DFS的次数。
代码如下:
#include <bits/stdc++.h> using namespace std; int mp[105][105]; int res = 0; int dx[4] = {-1, 1, 0, 0};//上下左右搜索数组 int dy[4] = {0, 0, -1, 1}; //上下左右搜索数组 int n, m, k, a, b; void dfs(int x, int y) { mp[x][y] = 1;//将黑棋变为白棋 for (int i = 0; i < 4; ++i) { int nx = dx[i] + x;//下一个棋子的行 int ny = dy[i] + y;//下一个棋子的列 if (nx >= 1 && nx <= n && ny >= 1 && ny <= m && mp[nx][ny] == 0)//周围的棋子是否合法 { dfs(nx, ny);// } } } int main(void) { cin >> n >> m >> k; memset(mp, 0, sizeof(mp)); for (int i = 0; i < k; ++i) { cin >> a >> b; for (int i = 1; i <= m; ++i)//反转第a行 { mp[a][i] = !mp[a][i]; } for (int i = 1; i <= n; ++i) { mp[i][b] = !mp[i][b];//反转第b列 } mp[a][b] = !mp[a][b];//棋子自我反转,因为前面的两次反转抵消了 } for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j)//遍历地图 { if (mp[i][j] == 0) { dfs(i, j); res++;//记录DFS搜索的次数,也就是黑块的个数 } } } cout << res << endl; }
这道题没卡时间,不用剪枝都能过,是真的很良心了,不过比赛的时候数据出了问题浪费了我不少时间,水题一枚。
D 多次方程求解
因为解可能有多个,且还可以扩展到复数域上的解,所以请输出任意一个实数域上的正数解即可。
当然,计算机无法表示精确解,所以你只需要保证答案误差小于0.0001即可。
单组输入。
第一行输入一个正整数n。
输出答案,本题为SPJ,只需要你的输出答案通过计算得到的n与标准输入n之差的绝对值小于0.0001(即准确到小数点后4位即可)即算答案正确,格式不限。
3
1.330325
解题思路:这道题就是浮点二分,不断缩小x的范围然后精确到0.0001就行
代码如下:
#include <bits/stdc++.h> using namespace std; int n; double cal(double x) { double kk = n; double sum = 0.0; for (int i = 1; i <= n; ++i) sum += pow(x, i) / (i * 1.0);//表达式计算 return sum - kk;//返回把n移到左边的值,方便二分计算 } void defen() { double l = -1.0, r = 2.0; double mid = l + (r - l) / 2.0; double key = cal(mid); while (fabs(key) > 0.000001)//注意用fabs()防止key小于0,只要大于精度就行,我为了保险多加了几个0 { if (key > 0) r = mid; else l = mid; mid = l + (r - l) / 2.0; key = cal(mid); } cout << mid << endl; } int main(void) { cin >> n; defen();//调用二分函数 }
二分水体,不要被题目吓到了。
E 约瑟夫升级
你一定听说过约瑟夫问题,即n个人围成一圈从第一个人开始隔k个人选取一个淘汰,最后留下来的为胜者,那么你现在要来解决一个类似约瑟夫问题的问题。
对于一个长度为n的序列,每次去掉第一位,将第二位放在最后一位的后面。最后留下的数即是答案。
例如对于数列1 2 3 4 5 6将会经过如下变形
* 1 2 3 4 5 6
* 3 4 5 6 2
* 5 6 2 4
* 2 4 6
* 6 4
* 4
所以答案是4
单组输入。
第一行一个正整数n(n<=100000),代表数列的长度
第二行包含n个用空格隔开的序列,代表原序列。
求给定序列的答案。
5
6 4 8 7 1
4
解题思路:说是约瑟夫环的升级,其实是消弱,并没有卡时间,所以直接模拟能AC,如果卡时间的话就要用公式法,本题开一个队列直接按照要求模拟最后剩下来的一个元素就是答案输出就行。
代码如下:
#include <bits/stdc++.h> #include <queue> using namespace std; int main(void) { queue<int> p; int n, k; cin >> n; for (int i = 0; i < n; ++i) { cin >> k; p.push(k); } while (p.size() != 1)//当队列的长度不为1就循环 { p.pop();//把第一个人淘汰 p.push(p.front());//把第二个人放在队尾 p.pop();//删除第二个人在队首的位置 } cout << p.front() << endl; }
F 字符画打印,很简单哒
输出下面那副字符画即可,注意空格
本题无输入数据
输出下面那副字符画即可,注意空格
本题无输入数据
### ###### ## ##
## ## ## ## ### ###
## ## ## #### ####
## ## ## ## ### ##
######### ## ## ##
## ## ## ## ## ##
## ## ###### ## ##
解题思路:签到题,printf出来就行符号是ACM
代码如下:
#include <bits/stdc++.h> using namespace std; int main(void) { printf(" ### ###### ## ##\n"); printf(" ## ## ## ## ### ###\n"); printf(" ## ## ## #### ####\n"); printf("## ## ## ## ### ##\n"); printf("######### ## ## ##\n"); printf("## ## ## ## ## ##\n"); printf("## ## ###### ## ##\n"); return 0; }
G 黄金矿工
大家都玩过黄金矿工这个游戏吧, 当矿工挖到一个金块时, 需要花费一定的时间。假设现在有n个金块, 每个金块的价值为xi元, 所需要花费的时间为yi秒, 试问在m秒内矿工最多能获得多少元的金块?若剩余时间不足以拉上某个金块则可以按比例获得对应的价值
单组输入
第一行包含两个整数n, m(1<=n, m<=10000)
接下来n行每行包含两个整数xi, yi(1<=xi<=10000, 1<=yi<=10)
输出m秒内矿工最多能获得多少元的金块, 答案保留到小数点后两位
2 10
10 9
1 1
2 10
10 9
1 2
11.00
10.50
对于第一组数据, 10秒足够把所有的金块都拉上来, 所以是11元
对于第二组数据, 首先拉上来价值10的金块, 剩下的1秒只能拉到0.5, 因为第二个金块至少需要2秒钟, 而剩余时间只剩下1秒, 因此按照时间之比获得价值0.5金块
解题思路:本题主要就是排序,用一个结构体表示每个金块的价值和花费时间还有单位时间的价值,然后按照单位时间的价值排序,从大到小,然后遍历贪心选取尽可能的金块,对于最后时间不够转化为相应比例的价值。具体情况看代码。
代码如下:
#include <bits/stdc++.h> using namespace std; struct node { double x, y; double v;//金块单位时间的价值 } a[10005]; bool cmp(struct node a, struct node b)//sort的cmp函数,具体使用方法可以看我的博客有介绍 { return a.v > b.v; } int n, m; int main(void) { double sum = 0.0; cin >> n >> m; for (int i = 0; i < n; ++i) { cin >> a[i].x >> a[i].y; a[i].v = a[i].x / a[i].y; } sort(a, a + n, cmp); for (int i = 0; i < n; ++i) { if (m > a[i].y)//如果时间大于这个金块花费的时间就直接加上它的价值 { sum += a[i].x; m -= a[i].y; } else { sum += a[i].x * m / a[i].y;//把剩下的时间转化为相应比例的价值,然后退出循环 break; } } printf("%.2lf\n", sum); return 0; }
这道题我以前做过一道相似的题目(不过找不到了),接替的关键也就是单位时间的价值排序。
H 天之杯
已知一个圆柱体的底面半径为r,高为h,若在这个圆柱体上切一刀后,其对应的切面面积最大应该是多少?Π等于4
单组数据
第一行包含两个整数r,h(1<=r,h<=1000)
输出切面的最大面积,结果保留两位小数
10 10
447.21
解题思路:这道题就是纯数学题,要使切面最大则是斜着切下来。切面为椭圆形计算公式为Π*a*b
a=sqrt(4*a^2+h^2)/2;
b=r;
∵Π=4;
∴S=2*r*sqrt(4*a^2+h^2);
代码如下:
#include <bits/stdc++.h> using namespace std; int main(void) { double r, h; cin >> r >> h; printf("%.2lf", 2 * r * sqrt(4 * r * r + h * h)); return 0; }
_______________________________
以上就是本次比赛的全部题目,都是很简单基础的,大家都能做就看谁做的快,所以这次未能AK算是一点小小的遗憾吧
如过本文有错误或者哪里有问题请在评论区留言,欢迎大家留言
当然也可私聊我:QQ:1196991321
over,玩游戏去了