A.题目难度
额,第一题我又出错数据了,当时我写完题解代码没有交,直接跑的数据导致,出现数据错误了,给大家道个歉。
这是一道经典模拟题,一般这类题目题干较长,需要仔细分析条件并写出代码。从这一道题中解析题目条件,题目难度不能为负数;题目难度的平均数要大于等于题目数量,小于等于题目数量二倍;最难的和最简单的差值要大于等于5,小于等于10。分析出这些条件接很好写了。
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int a[N];
int main()
{
int t;
cin>>t;
while(t--)
{
int n,bj=0;
cin>>n;
double x=0;//用于计算平均难度,因为可能会有小数情况,所以用double存
for(int i=0;i<n;i++)
{
cin>>a[i];
x+=a[i];
}
for(int i=0;i<n;i++)
{
if(a[i]<0){cout<<"小劉你又出错题了"<<endl;bj=2;break;}//遍历一边是否有负数存在
}
if(bj==2)continue;//若存在负数,则不用再进行别的操作了
sort(a,a+n);//排序,再去判断一下最大最小的差
int p=a[n-1]-a[0];//最大和最小的差
x=x/n*1.0;
if(p<5||p>10)bj=1;//判断最大和最小的差的范围
if(x<n||x>2*n)bj=1;//判断平均难度的范围
if(bj==1)cout<<"小劉出的题目难度好古怪"<<endl;
else cout<<"小劉这次出的题目没问题"<<endl;
}
}
B.离散数学
这其实就是一道定义题,加上暴力优化就行了。首先要先看懂传递性的定义,这其实就不用最多说了,但是有些人上课不听讲,那就再解释一下吧。假设有两个序偶<x,y>,<y,z>,所谓传递性就是一个序偶中的第二个数,即y,如果能够在R中找到以y为第一个数的序偶,那么就需要判断以y为第二个数的序偶的第一个数,即x,以y为第一个数的序偶的第二个数,即z,组成的序偶<x,z>,若所有满足这样条件的序偶组合出的序偶都存在R中,则证明有传递性。在通俗一点的话,拿几个例子来说例如R={<1,1>,<1,3>,<1,2>},这里边<1,1>和<1,2>能组合出<1,2>,并且它在R里,<1,1>和<1,3>能组合出<1,3>,并且它在R里,<1,3>在R中没有以3开头的则不用管它,同理<1,2>也是最后,因为能够组合的全部在R中,不能组合的不用管,所以判断R具有传递性。这道题我跑需随机数的时候没跑超过x的数,现在已经加上去了,需要判断一下。
第一种方法,它的复杂度是n*n,根据数据大概在1e7左右
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
typedef pair<int,int> PII;
vector<PII> a;
bool p[N][N];
int main()
{
int bj=0;
int n,x;
cin>>n>>x;
for(int i=0;i<n;i++)
{
int b,c;
cin>>b>>c;
p[b][c]=true;//将存在过的序偶全部标记方便后续处理
a.push_back({b,c});//以数对的形式放入容器中
if(a[i].second<=0||a[i].first<=0||a[i].first>x||a[i].second>x)bj=1;
//判断是否会有序偶超出1到x的范围,超出直接判断没有传递性
}
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
if(a[i].second==a[j].first)//遍历到有某一个的序偶的第二个数等于另一个序偶的第一个数
//进入判断条件
{
if(p[a[i].first][a[j].second]!=true){bj=1;break;}//存在则不处理,不存在则直接
//直接跳出,直接判断没有传递
//性
}
}
if(bj==1)break;
}
if(bj==1)cout<<"No";
else cout<<"Yes";
}
第二种方法,时间复杂度大概是x*x*x,根据数据大概1e5左右
#include<bits/stdc++.h>
using namespace std;
const int N=110;
typedef pair<int,int> PII;
vector<PII> c;
bool bj[N][N];
int main()
{
int t;
cin>>t;
while(t--)
{
int n,x;
cin>>n>>x;
for(int i=0;i<n;i++)
{
int a,b;
cin>>a>>b;
c.push_back({a,b});
bj[a][b]=true;//将存在过的序偶标记,方便后续处理
}
for(int i=0;i<n;i++)if(c[i].first>x||c[i].first<0||c[i].second<0||c[i].second>x)
{cout<<"No\n";continue;}//如出现不在1到x范围的数字则直接不存在传递性
int p=0;//一个标记
for(int i=1;i<=x;i++)
{
for(int j=1;j<=x;j++)
{
if(bj[i][j]==true)//遍历所有序偶的可能性,若存在则直接进入
{
for(int k=1;k<=x;k++) //当某一个序偶存在时,去遍历以该序偶的第二个数字开
{ //头的序偶,若不存则不做处理,若存在则需要判断组合
if(bj[j][k]==true) //出来的新的序偶是否存在,若不存在则直接跳出输出没
{ //有传递性,否则就是有传递性
if(bj[i][k]!=true){p=1;break;}
}
}
}
if(p==1)break;
}
if(p==1)break;
}
if(p==1)cout<<"No\n";
else cout<<"Yes\n";
}
}
C.数学建模
这是一道双指针优化的题目,正常先直接双重for循环,再优化。根据题目要求,先找出所有以1开头,以x结尾的数字串,然后再找出它们中最长的输出即可。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N];
int main()
{
int n,x;
cin>>n>>x;
for(int i=0;i<n;i++)cin>>a[i];
int t=-1,bj=0;//t用来标记,如果不存在答案数字串的话,则t的数值也不会更新,直接输出就行
for(int i=0,j;i<n;i++)
{
if(a[i]==1)//当开头是1的时候进入
{
j=i+1;//从1的下一位开始遍历
bj=0;
while(a[j]<=x&&a[j]>1)
{
if(a[j]==x){bj=1;break;}//遇见x跳出的话,表示是符合条件的数字串,更新t的值
//不是遇见x的话,便是该数字串不符合条件,不更新t的值
j++;
}
}
if(bj==1)t=max(t,j-i+1);
}
cout<<t;//最后输出即可
}
D.序列
这是一道二分的题目,实际上如果有人学过bound的话,这道题可以直接秒掉的。通过二分去寻找目标数字的左边界,当二分结束的时候,需要判断是否存在,因为二分肯定会结束,但不一定存在答案,所以需要在二分结束时去判断是否存在。
这一道题不能使用endl换行,因为endl需要清空缓冲区,所以会增加时间导致超时,最好直接使用“\n”换行
第一种方法,二分解法
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int a[N];
int main()
{
int n,q;
cin>>n>>q;
for(int i=0;i<n;i++)cin>>a[i];
while(q--)
{
int x;
cin>>x;
int l=0,r=n-1;//确定左右边界,开始二分
while(l<r)
{
int mid=l+r>>1;//每次取中点判断
if(a[mid]>=x)r=mid;//若符合条件,则更新右边界
else l=mid+1;//否则,更新左边界
}
if(a[l]!=x)cout<<"-1"<<"\n";//最后判断是否存在,不存在输出-1
else cout<<l+1<<"\n";//否则,输出边界加1,因为我的数组下标是从1开始的
}
return 0;
}
第二种解法,map解法
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int a[N];
map<int,int>mp;
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(mp[a[i]]==0)//将每个数第一次出现的位置存起来
mp[a[i]]=i;
}
for(int i=1;i<=m;i++)
{
int t;
cin>>t;
if(mp[t]==0)//判断是否出现过,出现过的话,值应该为下标,未出现过的话,值为0
cout<<-1<<"\n";
else cout<<mp[t]<<"\n";
}
return 0;
}
第三种解法,bound,STL大法
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int q[N];
int main(){
int n,m;cin>>n>>m;
for(int i=0;i<n;i++)cin>>q[i];
while(m--){
int x;cin>>x;
int t=lower_bound(q,q+n,x)-q;//lower_bound能够直接返回某一个数字在序列中出现过的第一个
//位置的地址,所以需要减去数组的首地址,数组首地址即为数组
//名,固直接减数组名即可确定某一个数字第一次出现的位置
if(q[t]==x)cout<<t+1<<'\n';
else cout<<-1<<'\n';
}
return 0;
}
E.朋友
经典并查集的模板,就增加了一个询问的部分,所以在版子增加一些即可
#include<bits/stdc++.h>
using namespace std;
const int N=5100;
int q[N];
int find(int x){
if(q[x]!=x)q[x]=find(q[x]);
return q[x];
}//并查集经典函数,不做解释
int main()
{
int n,m,x;
cin>>n>>m>>x;
for(int i=1;i<=x;i++)q[i]=i;
for(int i=1;i<=n;i++)
{
int x,y;
cin>>x>>y;
if(find(x)!=find(y))q[find(x)]=find(y);
}
while(m--)//询问的部分
{
int p;
cin>>p;
if(find(p)==find(1))cout<<"Yes\n";
else cout<<"No\n";
}
return 0;
}
0
F.馈赠
签到题
#include<bits/stdc++.h>
using namespace std;
int main()
{
string a;
cin>>a;
cout<<a.size();
return 0;
}
G.巧克力
经典BFS,和之前的农场的水坑几乎差不多,只需要在加一个求面积的即可
#include<bits/stdc++.h>
using namespace std;
const int N=120;
typedef pair<int,int> PII;
int a[N][N];
bool b[N][N];
int x1=0,s1=0,s2=0;
int dx[4]={-1,1,0,0},dy[4]={0,0,1,-1};// 四个方向
void bfs(int x,int y)
{
x1++;//求个数
queue<PII> q;
q.push({x,y});
while(q.size())
{
s1++;//求面积
PII p=q.front();
q.pop();
for(int i=0;i<4;i++)
{
int xx=p.first+dx[i],yy=p.second+dy[i];
if(b[xx][yy]==true)
{
b[xx][yy]=false;
q.push({xx,yy});
}
}
}
s1--;//最后会多走一次,减去
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
cin>>a[i][j];
if(a[i][j]==0)b[i][j]=true;
}
}
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(b[i][j]==true){s1=0;bfs(i,j);s2=max(s2,s1);//更新最大值}
}
}
cout<<x1<<" "<<s2;
return 0;
}