2020牛客暑假多校训练第三场总结
前言:
关于队伍:
首次组队,感觉和自己单打独斗相比还是区别很大的!
这次比赛是**“三缺一(Code At Three)”**第一次以团队身份参与训练,队员在比赛中的交流配合还算是比较顺利,实际效果也是比较理想。但同时也暴露了个人思维的单调、配合默契度不高等问题,相信这些到最后都可以顺利解决。
关于比赛:
这次比赛,首先出题人把签到题安排在了L,也就是最后一个…(这个你不想跟榜都没办法了)果然出题人永远是爸爸…
除了L是一道赤裸裸的水题以外,A,B题是两道相对简单的思维题(说相对简单是因为别的也没做)
A题基于贪心的思维,间接考了一点点动态规划的思想,但是是不是纯的dp
题还有待验证,至少现在知道用01栈是可以直接写的。B题则是一道怎么看怎么像模拟但实际上是一道考规律的数学题,而且最恶心的是他严卡了scanf
和cin
,即便是在循环外使用一次cin
都会直接给你T
回来(出题人说这是顺应时代)
个人总结:
这次训练里自己提供了A,B的思路,然后自己手打了一道B。这次比赛还是很锻炼思维,就比如A题,如果单纯按照有鱼拿鱼,有饵钓鱼的思路做,最后的答案肯定不对的,举个例子来说,如果你此时手里已经有了一个鱼饵,但是接下来你要经历的阶段是100
,如果按照上面说的思路,那最后你会有1条鱼,但是如果在1阶段做了鱼饵,剩下两个0阶段钓鱼那就会有2条。我提出这个反例后队友也是很给力直接用了01栈AC了。
这次比赛还是暴露了一些问题。首先个人思维有待提高,对于不同题目的敏感度还是不够,需要课下不断训练培养。其次队伍建设还需要加把劲,尽快把github
上的相关配置完善好。再有就是针对我个人还要多去了解队员们,争取能把团队的氛围w做到极致。
题解:(除L题,部分来源于队友)
A.Clam and Fish
题目描述:
大概的意思就是一个人现在在一个湖边钓鱼,现在有四种不同的钓鱼状态
状态0:你现在什么都没有
状态1:你有一个河蚌但是没有鱼
状态2:你有一个鱼和一个河蚌
状态3:你有鱼没有河蚌
现在每个阶段你都可以进行不同的操作,如下描述:
1.如果你现在就有一条鱼,你可以只拿鱼。
2.如果你现在有河蚌,你可以做一份鱼饵。
3.如果你手里有鱼饵,那你每次只可以用一份鱼饵钓一条鱼。
4.你什么都不干。
询问你可以获得的最大的鱼的数目。
思路:
其实上面总结里已经写到了,我们肯定没法采用有鱼拿鱼,没鱼做饵的思路做,因为一旦遇到100
的情况就没法做了。
但是有一点我们确认的是,当我们处于3,4种情况时,我们手里总是有鱼的,这个时候我们直接拿鱼总会比做鱼饵留到后面用要实惠,所以毫无疑问3,4种情况我们需要直接拿鱼。
那么对于0和1呢?我们发现我们做鱼饵的次数受限于1之后0的个数,我们就可以使用类似括号匹配的思想使用01栈。10作为一组匹配,那么最后如果栈中还有多的1过程,那我们就对半分,一半做鱼饵,一半钓鱼。
AC代码
#include<iostream>
#include<algorithm>
#include<math.h>
#include<cstdio>
#include<string>
#include<string.h>
#include<list>
#include<queue>
#include<sstream>
#include<vector>
#include<set>
#include<map>
#include<deque>
#include<stack>
using namespace std;
#define debug(x) cout<<"###"<<x<<"###"<<endl;
const int INF=0x3f3f3f3f,mod=1e9,Maxn=1e6;
typedef long long ll;
int main(){
int t;
cin>>t;
int a;
while(t--){
stack<char> stk;
string s;
int n;
cin>>n;
int ans=0;
cin>>s;
for(int i=0;i<n;i++){
if(s[i]=='1'){
stk.push((s[i]));
}
if(s[i]=='2'){
ans++;
}
if(s[i]=='3'){
ans++;
}
if(s[i]=='0'){
if(!stk.empty()){
stk.pop();
ans++;
}
}
}
int cnt=0;
while(!stk.empty()){
stk.pop();
cnt++;
}
cout<<ans+cnt/2<<endl;
}
return 0;
}
B.Classical String Problem
题目描述:
给了你一个字符串S(1~S.length()
),现在给你这样的操作要求:
输入一个字符C和一个整数X,C为A时表示询问字符串的第X个字符并输出,C为M时表示移动字符串的部分字符,移动要求如下:X小于0时,表示把字符串的后x个字符移动到原字符串前面,X大于0时,表示把字符串的前X个字符移动到原字符串后面。
思路:
这个题貌似一看就是模拟题。
但是他还真不能模拟…(某彪:“这不一看就知道”)
这个题硬模拟的话会卡时间。所以只能另找规律。
我们发现,不管我们对字符串进行多少次变换,每次变换后的每个字符的坐标和对应原字符串的每个字符的坐标之间总是存在着某种关系。这里我们把X小于零的情况认为是和(length + X)等价也就是移动后X个字符相当于移动了前(length - x)个字符。我们可以看出来,如果前面的X个字符移动到后面,相当于每个字符的位置都变化成了(i + x) % len
;我们不妨对字符串位置的变化量进行一个积累,然后当C为A时可以直接通过改变其对应的X来找到在原字符串中其对应的字符。
见代码:
AC代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define ll long long
using namespace std;
const int maxn = 2e6 + 19;
char ss[maxn];
int main()
{
scanf("%s",ss);
int len = strlen(ss);
int t;
ll temp = 0;
scanf("%d",&t);
while(t--){
char ch;int s;
scanf(" %c%d",&ch,&s);
if(ch == 'A') {
printf("%c\n",ss[((s + temp - 1) % len)]);//对应到原数组中
}
else{
if(s > 0) temp += s;//对变化量累计
else temp += (len - abs(s));
}
}
return 0;
}
C.Operation Love
题目描述:
题目给了一个爱丽丝的右手手掌印,并且告诉我们爱丽丝的手掌的位置可以平移和旋转,但是各个边的大小都不会变,现在给出了手掌上20个点的坐标,起点不知,顺时针逆时针不知,但是肯定是其中一种。
现在给了你20个坐标,要求你判断这个是爱丽丝的左手还是右手
思路:
这个题很有意思的点就是这个手掌的朝向不一定。
但是分析后发现,我们可以通过最坏20次循环,找到手掌底部的两个点,他们之间相距9单位长度,同时我们也注意到,长度为6和长度为8的两边的位置对于左右手而言是确定的,我们就可以使用叉积,通过叉积的正负和对应边的大小判断左右手。
注意一下,这个题卡精度卡的很严格,大概1e-5或者1e-4这个位置都可以过,但是1e-6不行。
详情见代码。
AC代码:
#include<bits/stdc++.h>
using namespace std;
struct point{
double x,y;
}p[20];
double eps=1e-5;//浮点数不能直接用==比较
double Distance(point p1,point p2){
return (sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)));
}
double cross(point a,point b,point c){//计算叉积
return (a.x-c.x)*(b.y-c.y)-(a.y-c.y)*(b.x-c.x);
}
int main(){
int t;
scanf("%d",&t);
while(t--){
bool flag=0;
for(int i=0;i<20;i++) scanf("%lf%lf",&p[i].x,&p[i].y);
for(int i=0;i<20;i++){
if(fabs(Distance(p[i],p[(i+1)%20])-9)<eps){
if((cross(p[i],p[(i+2)%20],p[(i+1)%20])<0)^(fabs(Distance(p[(i+1)%20],p[(i+2)%20])-8)<eps)) flag=1;
}
}
if(!flag) printf("right\n");
else printf("left\n");
}
return 0;
}