2018/12/2 新生中石油多校第一次联训补题题解

感慨

第一次多校联训,发现跟其他学校的18级的同学差距还是有的,但是总体来说差距不是很大我以为要直接100多名没想到还水了一个银牌最终26名。不过也有可能是题目太简单的缘故吧。。。

A 录取分数线(基础编程能力)

完全的送分题。。。与一血差1s可还行。实际上这个题在洛谷也有类似的题

解法

注意读懂题意,题目中要求是要忍痛割爱,所以如果有并列的分数,那么一定是要把这些并列的都去除的,而不是多多的引进。这是一个坑的地方

①排序(sort)

②找到分数线那一位的分数,然后找一下分数线以下的那个人的分数

(1)如果那个人的分数和这个分数线的压线人的分数一样,说明要忍痛割爱了,往上找一个大于压线成绩的输出

(2)如果不同说明不需要忍痛割爱,直接输出压线成绩

代码

#include <bits/stdc++.h>
using namespace std;
int num[1000005];
int main()
{
  ios::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  int n,m;
  cin>>n>>m;
  for(int i=0;i<n;i++)
  cin>>num[i];
  sort(num,num+n);//①排序(sort)
  if(num[n-m]==num[n-m-1])//(1)如果那个人的分数和这个分数线的压线人的分数一样,说明要忍痛割爱了,往上找一个大于压线成绩的输出
  {
    for(int i=n-m+1;i<n;i++)
    if(num[i]>num[n-m])
    {
      cout<<num[i];
      break;
    }
  }
  else//(2)如果不同说明不需要忍痛割爱,直接输出压线成绩
  cout<<num[n-m];
}

B 电子警察(基础编程能力+细心)

直接白给了好几次罚时,我以后再也不敢反着写条件了。

解法

这个题很考验耐心与细心的程度

①处理输入输出

(1)把时间与方向与车牌号分离

(2)把时间换算成秒

②因为要寻找是不是违章并且要按照顺序输出,那么我们可以直接看这个换算成秒的时间在这个左中右三个灯的周期里处于什么位置

(1)这里我觉得要注意一下最终的时间也是有的。比如样例一个周期是65秒,换算成最小周期后的65s也是有意义的所以我们的换算公式应该是下面这个

re=(总秒数-1)%总周期+1

③按照方向的那个指标分成三类一一讨论即可(这个地方注意他们的所有的区间都是左开右闭的。。。坑点之一)

也就是说15是(0,15]是l 30是(15,30]是s

代码

#include <bits/stdc++.h>
using namespace std;
int num[1000005];
int main()
{
  ios::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  int l,s,r,n;
  cin>>l>>s>>r>>n;
  while(n--)
  {
    string a;//①处理输入输出
    cin>>a;
    string h,m,se;
    for(int i=0;i<2;i++)
    h+=a[i];
    for(int i=2;i<4;i++)
    m+=a[i];
    for(int i=4;i<6;i++)
    se+=a[i];
    stringstream a1,a2,a3;
    a1<<h;a2<<m;a3<<se;
    int h1,m1,s1;
    a1>>h1;a2>>m1;a3>>s1;
    //cout<<h1<<" "<<m1<<" "<<s1<<"\n";
    int sum=h1*3600+m1*60+s1;
    int ca;
    int te=(sum-1)%(l+s+r)+1;//re=(总秒数-1)%总周期+1
    //cout<<te<<"\n";
    if(a[6]=='L')
    ca=1;
    else if(a[6]=='S')
    ca=2;
    else if(a[6]=='R')
    ca=3;
    if(ca==1)//③按照方向的那个指标分成三类一一讨论即可
    {
      if(te>l)
      {
        for(int i=7;i<a.size();i++)
        cout<<a[i];
        cout<<"\n";
        continue;
      }
    }
    else if(ca==2)
    {
      if(te>s+l||te<=l)
      {
        for(int i=7;i<a.size();i++)
        cout<<a[i];
        cout<<"\n";
        continue;
      }
    }
    else if(ca==3)
    {
      if(te<=s+l)
      {
        for(int i=7;i<a.size();i++)
        cout<<a[i];
        cout<<"\n";
        continue;
      }
    }
  }
}

C 查找特定的合数(数论)

完全的数论题。。。

首先我们得看出来这个问题的本质,他就是来分解质因数的。因为数据过大普通的算法肯定会T,但是还是需要讲一下普通的算法如何实现

分解质因数(O(根号n))

①对于一个需要分解质因数的n,我们需从2开始到n枚举各个数

②while如果n能够整除i那么就把i放进vector。然后因为这个题的缘故需要开一个bk数组记录一下如果vac中没有这个数那么入队否则不入队

③进行n/=i

④这个从2到n可以优化成从2到根号下n

⑤对vac的大小进行判定如果等于x那么计数器加一如果计数器等于m,那么输出此时的i即可

TLE的代码

#include <bits/stdc++.h>
using namespace std;
int bk[200005];
int main()
{
  ios::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  int m,x,ans=0;
  cin>>m>>x;
  for(int i=2;i<=200000;i++)
  {
    int k=i;
    vector<int> vac;
    memset(bk,0,sizeof(bk));
    for(int j=2;j*j<=i;j++)
    {
      while(k%j==0)
      {
        if(!bk[j])
        {
          vac.push_back(j);
          bk[j]=1;
        }
        k/=j;
      }
    }
    if(vac.size()==x)
    ans++;
    if(ans==m)
    {
      cout<<i;
      break;
    }
  }
}

正解(打表,打一半即可)

用当前的数去除以素数就能分解质因数了。之后再判断一下枚举的时候那个数是不是合数即可

代码

#include <bits/stdc++.h>
using namespace std;
int prime[]={这里是素数表};
int isp(int n)
{
  for(int i=2;i*i<=n;i++)
  if(n%i==0)
  return 0;
  return 1;
}
int solve(int n)
{
    int sum=0;
    for(int i=0;prime[i]<=n;i++)
    if(n%prime[i]==0)
    {
        n/=prime[i];
        sum++;
    }
    return sum;
}
int main()
{
  ios::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  int n,m,re=0;
  cin>>n>>m;
  for(int i=2;i<=200000;i++)
  {
    if(isp(i))
    continue;
    if(solve(i)==m)
    re++;
    if(re==n)
    {
        cout<<i;
        break;
    }
  }
}

D 传话游戏(强连通分量)

这个真的是小学题吗???小学就有图论?

解法

可以把这些回路看成裸的强连通分量

①tarjan

②遍历每一个节点,如果除了这个节点之外还有和这个节点染色相同的节点,那么就可以断定这个可以收到传话

(1)因为数据量比较小,我们比较的时候可以直接用两个for比较时间完全也不会有问题

代码

#include <iostream>
#include <vector>
#include <map>
using namespace std;
vector<int> G[10005];
map<int,int> stack,vis,dfn,low,color,ans;
map<int,map<int,int> > bk1;
int deep=1,top=1,sum;
void tarjan(int n)
{
  dfn[n]=low[n]=++deep;
  vis[n]=1;
  stack[++top]=n;
  for(int i=0;i<G[n].size();i++)
  {
    int p=G[n][i];
    if(!dfn[p])
    {
      tarjan(p);
      low[n]=min(low[p],low[n]);
    }
    else if(vis[p])
    low[n]=min(low[p],low[n]);
  }
  if(dfn[n]==low[n])
  {
    color[n]=++sum;
    vis[n]=0;
    while(stack[top]!=n)
    {
      color[stack[top]]=sum;
      vis[stack[top--]]=0;
    }
    top--;
  }
}
int main()
{
  ios::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  int n,m;
  cin>>n>>m;
  while(m--)
  {
    int t1,t2;
    cin>>t1>>t2;
    if(!bk1[t1][t2])
    G[t1].push_back(t2),bk1[t1][t2]=1;
  }
  for(int i=1;i<=n;i++)
  if(!dfn[i])
  tarjan(i);
  for(int i=1;i<=n;i++)
  for(int j=1;j<=n;j++)
  if(i!=j&&color[i]==color[j])
  {
    ans[i]=1;
    break;
  }
  for(int i=1;i<=n;i++)
  if(ans[i])
  cout<<"T\n";
  else
  cout<<"F\n";
}

E 谁是冠军(排序)

这个题还因为sort昏迷白给了两次

解法

直接结构体排序即可,没有坑

代码

#include <bits/stdc++.h>
using namespace std;
struct node
{
  int id,n;
}num[1000000];
bool cmp(node a,node b)
{
  return a.n>b.n;
}
int main()
{
  ios::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  int n;
  cin>>n;
  for(int i=1;i<=n;i++)
  {
    int t1,t2,t3,t4;
    cin>>t1>>t2>>t3>>t4;
    int sum=t2*3+t3*1-t4;
    //cout<<sum<<"\n";
    num[i].n=sum;
    num[i].id=t1;
  }
  sort(num+1,num+n+1,cmp);
  cout<<num[1].id;
}

F 搭积木的诀窍(数学)

这个题我绝对不是正解,我直接模拟做的。这个应该用数列的一些知识。我现在也记不清了。。。只好硬来

解法

因为不是正解所以不多说了。思路就是把每次的和记录下来并且模拟运算每一次的和即可

代码

#include <bits/stdc++.h>
using namespace std;
int num[10000000];
int main()
{
  ios::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  int n;
  cin>>n;
  int last=1;
  int now,p=2,ft=1,re=0;
  int id;
  if(n==1)
  cout<<1<<"\n"<<0;
  else
  {
  while(1)
  {
    if(ft)
    now=last;
    else
    now=last+p++;
    ft=0;
    re+=now;
    id=p;
    num[id]=re;
    //cout<<re<<" "<<p<<" "<<num[id]<<"\n";
    if(re>n)
    {
      cout<<p-2<<"\n";
      cout<<n-num[id-1];
      break;
    }
    else if(re==n)
    {
      cout<<p-1<<"\n"<<0;
      break;
    }
    last=now;
  }
}
}

正解数学公式法(递推每层的通项公式)

代码

#include <bits/stdc++.h>
using namespace std;
int sum[1000];
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    for(int i=1;i<=1000;i++)
    sum[i]=(i*(i+1))/2+sum[i-1];
    int n;
    cin>>n;
    for(int i=1;i<=1000;i++)
    if(sum[i]>=n)
    {
        if(sum[i]==n)
        cout<<i<<"\n"<<0;
        else
        cout<<i-1<<"\n"<<n-sum[i-1];
        break;
    }
}

G 卡布列克常数(字符串基础)

这个也是一个水题

解法

把输入的数预处理一下即可。因为前导0的问题,这里我选择了用stringstream

代码

#include <bits/stdc++.h>
using namespace std;
char c[100];
int main()
{
  /*ios::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);*/
  string a;
  cin>>a;
  int sum=0;
  while(sum!=6174)
  {
    for(int i=0;i<a.size();i++)
    c[i]=a[i];
    sort(c,c+4);
    string r1="",r2="";
    for(int i=0;i<4;i++)
    r1+=c[i];
    for(int i=3;i>=0;i--)
    r2+=c[i];
    stringstream s1,s2,s3;
    s1<<r1;s2<<r2;
    int m1,m2;
    s1>>m1;s2>>m2;
    sum=m2-m1;
    printf("%d-%d=%d\n",m2,m1,sum);
    s3<<sum;
    s3>>a;
  }
}

H 扫雷游戏(思维)

这个题找到规律好做,找不到规律就费劲了。

解法

①我们实际上可以用当前的位置的地雷来预测下一个位置的地雷

②那么这样还需要判定一下第一个位置有没有地雷。好在这个题只需要判定有或者没有

③讲一下会出现矛盾的地方

(1)如果当前的地雷数+上一个格子的地雷数>当前格子的显示的数量,那么显然这个肯定是一种矛盾的情况

(2))如果当前的地雷数+上一个格子的地雷数=当前格子的显示的数量-2,那么这也是一种矛盾的情况为什么呢?

首先我们假设当前是1,那么1-2是-1这肯定不可能发生

如果当前是2,那么等于这一个格子加上上一个格子都是0,但是如果是二的话必须三个格子有两个格子是1但是最多这只有一个格子是1所以说矛盾

如果当前是3,同2的情况,三个格子必须都是1当前只有一个是1那么肯定矛盾

(3)如果当前的地雷数+上一个格子的地雷数=当前格子的显示的数量-1并且当前格子是最后一个格子,那么也是矛盾的。推理实际上同第二个矛盾的条件

如果是1那么是0肯定不可能,如果是2那么是1肯定不可能,如果是3那么是2肯定不可能

④没有矛盾的话就按照一个规律如果上一个加当前的地雷数=格子的地雷数-1那么就把下一个格子地雷数设置成1即可

(1)这里可以很好的理解为目前对于当期的格子地雷数贡献还少1

⑤因为只有两种情况:第一个格子有地雷,第一个格子没有地雷。所以说不是第一种情况,那么肯定是第二种情况!!!

代码

#include <bits/stdc++.h>
using namespace std;
int a[1000],b[1000],n,sum;
int ca1()
{
  b[1]=0;
  for(int i=1;i<=n;i++)
  {
    if(b[i]+b[i-1]>a[i]||b[i]+b[i-1]==a[i]-2||b[i]+b[i-1]==a[i]-1&&i==n)
    return 0;
    else if(b[i]+b[i-1]==a[i]-1)
    b[i+1]=1;
  }
  return 1;
}
void ca2()
{
  memset(b,0,sizeof(b));
  b[1]=1;
  for(int i=1;i<=n;i++)
  if(b[i]+b[i-1]==a[i]-1)
  b[i+1]=1;
}
int main()
{
  ios::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  cin>>n;
  for(int i=1;i<=n;i++)
  cin>>a[i];
  if(!ca1())
  ca2();
  for(int i=1;i<=n;i++)
  {
    cout<<b[i];
    if(b[i])
    sum++;
  }
  cout<<"\n"<<sum;
}
posted @ 2018-12-02 20:15  baccano!  阅读(336)  评论(0编辑  收藏  举报