PTA团体程序设计天梯赛


这次题目出得比前几次简单很多,但有几道题占用的时间太多,导致后面几题仓促写完,未能全部正确,还是得多练

目录

L1-2 九牛一毛

L1-3 小孩子才做选择,大人全都要 

L1-5 试试手气

L1-6 打PTA

L1-8 随机输一次

L2-3 智能护理中心统计

L1-7 矩阵列平移

L2-1 盲盒包装流水线

7-9 浪漫侧影 

L3-2 拼题A打卡奖励

7-11 考试周

L2-2 三足鼎立

7-13 狼人杀

L1-4 冠军魔术

7-15 谷歌的招聘


L1-2 九牛一毛

思路 :毫无疑问的签到题

void solve()
{
   int n;cin>>n;
   cout<<n/15<<' '<<n/20<<' '<<9*n*10;
}

L1-3 小孩子才做选择,大人全都要 

输入样例:

12 18

输出样例:

18 30
^_^
输入样例:

12 -18
输出样例:

12 0
T_T
 

思路: (1)如果两个盒子都有狗粮 那么就是笑脸

            (2)如果两个盒子都没有狗粮 就是平脸

            (3)如果其中一个有狗粮,另一个没狗粮,且有狗粮的数量大于没狗粮的,则铲屎官全都要能吃到的狗粮为a+b,否则为0,输出哭脸

void solve()
{
   int a,b;cin>>a>>b;
   if(a<0&&b<0)
   {
    puts("0 0");
    cout<<"-_-";
   }
   else if(a>0&&b>0)
   {
    cout<<max(a,b)<<' '<<a+b<<endl<<"^_^";
   }
   else
   {
    if(a+b>0) cout<<max(a,b)<<' '<<a+b<<endl<<"T_T";
    else 
    {
        cout<<max(a,b)<<' '<<0<<endl;
        cout<<"T_T";
    }
   }
}

L1-5 试试手气

思路: 深度搜索,搜索每个骰子时,从大到小遍历,用bool数组标记之前之前搜过的数字,回溯不用取消标记。由于是6个骰子整体要往下搜一遍,所以每次都要回溯到第一个骰子才能往下搜

constexpr int N=1010;
bool st[10][10];
int a[N];
int n,f,bian;
vector<int> temp,ans;

void dfs(int u)
{
    if(u==7&&!f)
    {
        bian++;
        if(bian==n)
        {
            ans=temp;
           f=1;
        }
        return ;
    }
    if(f) return ;

    for(int i=6;i>=1;i--)
    {
        if(!st[i][u])
        {
            st[i][u]=true;
            temp.pb(i);
            dfs(u+1);
            temp.pop_back();
            if(u>1) return ;
        }
    }
}
void solve()
{
   for(int i=1;i<=6;i++)
   {
    cin>>a[i];
    st[a[i]][i]=true;
   }

   cin>>n;
   dfs(1);
   for(int i=0;i<ans.size();i++)
   {
     if(i) cout<<' ';
     cout<<ans[i];
   }
}

L1-6 打PTA

思路: 简单的模拟题,先判断字符串结尾的符号是否为问号,如果是的话直接输出enen,否则就用find函数查找字符串中是否含有PTA

void solve()
{
   int n;cin>>n;
    getchar();
   while(n--)
   {
    string s;
       getline(cin,s);
    int len=s.size();
    if(s[len-1]!='?')
        puts("enen");
    else
    {
        if(s.find("PTA")!=-1)
            puts("Yes!");
        else puts("No.");
    }
   }
}


L1-8 随机输一次

输入样例:

3 2 4 1
ChuiZi
JianDao
Bu
JianDao
Bu
ChuiZi
ChuiZi
ChuiZi
JianDao
Bu
JianDao
Bu
ChuiZi
End

输出样例:

Bu
ChuiZi
ChuiZi
ChuiZi
JianDao
Bu
Bu
JianDao
ChuiZi
ChuiZi
ChuiZi
JianDao
JianDao

思路:简单模拟题,用一个变量记录你赢了多少次,在第K[i]+1局时输掉,然后赢的次数初始为0,到达第K[i+1]+1局时输掉,如此往复

constexpr int N=1010;
int K[N];

void solve()
{
  int n;cin>>n;
  for(int i=0;i<n;i++)
  {
    cin>>K[i];
    K[i]++;
  } 
  int cnt=0,k=0;
  string s;
  string a[3]={"ChuiZi","JianDao","Bu"};
  while(cin>>s,s!="End")
  {
    cnt++;
    if(cnt!=K[k%n])
    {
        if(s==a[0]) cout<<a[2]<<endl;
        if(s==a[1]) cout<<a[0]<<endl;
        if(s==a[2]) cout<<a[1]<<endl;
    }
    else
    {
       k++;
       cnt=0;
       if(s==a[0]) cout<<a[1];
       else if(s==a[1]) cout<<a[2];
       else cout<<a[0];
       cout<<endl;
    }
  }
  
}


L2-3 智能护理中心统计

输入样例:

10 23
EAST CNTR
ZJ EAST
SD EAST
WEST CNTR
SX WEST
HZ ZJ
JN SD
2 JN
8 JTH
6 XAHP
4 ZDYH
5 ZDYH
ZDYH HZ
HUZ ZJ
JX ZJ
1 JX
3 JN
WZ ZJ
XAHP XIAN
XIAN SX
YL SX
JTH XIAN
7 XAHP
Q EAST
T 1 YL
Q EAST
Q SX
T 8 ZDYH
Q HZ
Q HUZ
T 10 XAHP
Q CNTR
E

输出样例:

5
4
4
3
0
9

 思路:先将图画出来会更好理解

我们会发现这道题其实就是自底向上更新结果的递归,那么如何建立字符串之间边的关系呢,可以先将字符串哈希,然后用vector<int> ans[i]存储字符串i所包含的子节点,vector<int> str[i]存储这个字符串i中所包含的老人编号,由于指令T会将老人i转移至另一个管理节点j中,也就意味着老人i之前所在的管理节点将会减少一个人(或者这个老人之前不在任何一个节点,是一个“新人”),而管理节点j将会多一个人,所以我们需要存储每个老人所对应的管理节点编号,可以用map<int,int> ren存储。

关于如何dfs

自底向上更新,肯定是需要到达叶子节点时返回这个节点的str[i]的大小,也就是包含的老人的人数,每次向上更新都需要当前节点的老人数量加上子节点的老人数量,最后返回的就是要求的值了

constexpr int N=1e5+5;
int num;
vector<int> ans[N],str[N];
map<string,int> mp;
map<int,int> ren;

int dfs(int u)
{
    int len=str[u].size();
    if(ans[u].size()==0) return len;
    
   int res=str[u].size();
    for(auto t:ans[u])
    {
      res+=dfs(t);
    }
   // cout<<u<<' '<<res<<endl;
    return res;
}
void solve()
{
   int n,m;cin>>n>>m;
   for(int i=0;i<m;i++)
   {
     string a,b;cin>>a>>b;
     if(!isdigit(a[0])&&!mp[a]) mp[a]=++num;
     if(!mp[b]) mp[b]=++num;
       int A,B;
       if(!isdigit(a[0])) A=mp[a];
       else A=stoi(a.substr(0));
     B=mp[b];
     int f1=0;
     if(isdigit(a[0])) f1=1;
        if(f1)
        {
        
          ren[A]=B;
          str[B].eb(A);
        }
        else
        {
           ans[B].eb(A);
        }
   }  
   /* string aa="ZJ";
    cout<<str[mp[aa]].size()<<endl;
    for(auto t:ans[mp[aa]])
    {
        cout<<t<<' '<<str[t].size()<<endl;
    }
    cout<<dfs(mp[aa])<<endl;
    puts("----");*/
   char c;
   while(cin>>c,c!='E')
   {
    string s;
    if(c=='Q')
    {
        cin>>s;
        int a=mp[s];
        int res=0;
        res=dfs(a);
        cout<<res<<endl;
    }
    else
    {
       int ns;cin>>ns;
        string ss;cin>>ss;
        if(!ren[ns])
        {
            if(!mp[ss]) mp[ss]=++num;
            ren[ns]=mp[ss];
            str[mp[ss]].eb(ns);
        }
        else
        {
            int t=ren[ns];
            str[t].pop_back();
            if(!mp[ss]) mp[ss]=++num;
            ren[ns]=mp[ss];
            str[mp[ss]].eb(ns);
        }
    }
   }
}


L1-7 矩阵列平移

 思路:简单模拟题,用cnt记录这个偶数列要往下移多少个数,如果cnt到达k+1要将其初始化为1

constexpr int N=1e5+5;

int a[105][105];

void solve()
{
   int n,k,x;cin>>n>>k>>x;
   for(int i=1;i<=n;i++)
   {
    for(int j=1;j<=n;j++)
        cin>>a[i][j];
   }

   int cnt=0;
   for(int j=2;j<=n;j+=2)
   {
    cnt++;
    if(cnt==k+1) cnt=1;
       for(int i=n;i>cnt;i--) a[i][j]=a[i-cnt][j];
    for(int i=1;i<=cnt;i++)
        a[i][j]=x;
   }

   for(int i=1;i<=n;i++)
   {
     int sum=0;
       for(int j=1;j<=n;j++)
           sum+=a[i][j];
       if(i==1) cout<<sum;
       else cout<<' '<<sum;
   }
}

L2-1 盲盒包装流水线

思路 :每一行的徽章类型用栈存储(用数组存会爆空间,真是逆天了),每存完一行就将栈中的元素弹出,按顺序将编号哈希映射到对应的徽章类型

constexpr int N=1e6+5;

string a[N];
map<string,int> h;

void solve()
{
  int num=0;
  int n,s;cin>>n>>s;
  for(int i=0;i<n;i++)
  {
    cin>>a[i];
   h[a[i]]=1;
  } 
    int cnt=0;
  for(int i=0;i<n/s;i++)
  {
      stack<int> stk;
    for(int j=0;j<s;j++)
    {
        int x;cin>>x;
        stk.push(x);
    }
     while(!stk.empty())
     {
          h[a[cnt++]]=stk.top(); stk.pop();
     }
     
  } 
 
  int q;cin>>q;
  while(q--)
  {
    string x;cin>>x;
    if(!h[x]) puts("Wrong Number");
    else cout<<h[x]<<endl;
  }
}

7-9 浪漫侧影 

思路:赛后补的题,每次都写不出二叉树的题,平时也没去写,后面看题解感觉这题对熟悉二叉树那几个遍历顺序的人来讲应该算是板子题了,先通过先序和后序将二叉树构建出来,然后再通过层序遍历,保留每一层的最大和最小编号 ●'◡'●)

constexpr int N=1005;

int n,a[105],b[105];
vector<pair<int,int> > res[105];
void build(int al,int ar,int bl,int br,int level,int num)
{
    if (al > ar) return;
    res[level].pb({num,b[br]});
    int i;
    for (i = 1; a[i] != b[br]; ++i) ;  

    build(al,i-1,bl,bl+(i-1-al),level+1,num*2);  
    build(i+1,ar,br-1-(ar-i-1),br-1,level+1,num*2+1);  
}
void solve()
{
    cin>>n;
    for (int i = 1; i <= n; ++i) cin>>a[i];
    for (int i = 1; i <= n; ++i) cin>>b[i];
    build(1,n,1,n,1,1);
    cout<<"R:";
    for (int i = 1; res[i].size() > 0; i++)
    {
        sort(res[i].begin(), res[i].end()); 
        cout<<" "<<res[i].back().se;
   }
    cout<<endl<<"L:";
    for (int i = 1; res[i].size() > 0; i++)
    {
        cout<<" "<<res[i].front().se;
    } 
}


L3-2 拼题A打卡奖励

思路: 如果我们直接用01背包的模板来做的话,时间复杂度是O(N*M),已经超过了1e9,显然超时,所以我们可以定义dp[i]为获取i个金币需要花的最少时间。

constexpr int N=1005;

int dp[550000];
int a[N],w[N];
void solve()
{
  int n,m,sum=0;cin>>n>>m;
  for(int i=0;i<n;i++) cin>>a[i];
    for(int i=0;i<n;i++) cin>>w[i],sum+=w[i];
    mem(dp,0x3f)
   dp[0]=0;
   for(int i=0;i<n;i++)
    for(int j=sum;j>=w[i];j--) dp[j]=min(dp[j],dp[j-w[i]]+a[i]);
        int res=0;
    for(int j=sum;j>=0;j--)
        if(dp[j]<=m)
        {
            res=j;
            break;
        }
        cout<<res;
}

7-11 考试周

 

思路: 签到题

void solve()
{
   double a,b;cin>>a>>b;
   double res=a*1.0/b;
   printf("%.0lf/%.1lf=%.0lf",a,res,b);
}


L2-2 三足鼎立

思路:任何两国实力之和都大于第三国 ,我们可以联想到三角形的三边关系,所以我们只需要找到满足两个数之和大于第三个数同时两个数之差小于第三个数的个数,用lower_bound()查找第一个大于等于两数之和的位置,该位置之前的数都会小于两数之和,用upper_bound()查找大于两数之差的第一个位置,然后这两个位置相减就是能够取的第三个数的个数

constexpr int N=1005;
int n,p,a[100005];
void solve()
{
  cin>>n>>p;
  for(int i=0;i<n;i++) cin>>a[i];
    sort(a,a+n);
int sum=0;
    for(int i=0;i<n;i++)
    {
        int he=a[i]+p;
        int pos=lower_bound(a+1+i,a+n,he)-a;
        int k=upper_bound(a+i+1,a+n,abs(a[i]-p))-a;
        sum+=pos-k;
    }
    cout<<sum;
}

7-13 狼人杀

思路:深度搜索,先用honest[]数组初始化每个人都是好人,搜索时从n开始搜(因为结果的序列要最大),每个人搜两次是狼人或者不是狼人的情况,是狼人则honest[i]=-1,回溯时 honest[i]=1,接着搜i为好人的情况。

 如何剪枝?

1.如果狼人数量=m,我们再计算一下这n个人中撒谎的人数,如果第i个人说a[i]为狼人,但honest[a[i]]=1,或者第i个人说a[i]为好人,但honest[a[i]]=-1,则这个人撒谎,也就是满足honest[a[i]]*a[i]<0这个条件

2.如果总的撒谎人数达到L,则再计算狼人中的撒谎人数,如果第i个人满足honest[i]=-1,同时他说的a[i]满足honest[a[i]]*a[i]<0,那么这个狼人就是撒谎的

3.如果以上条件都满足可以直接输出答案,返回true,否则以上任一条件未满足就要返回false

constexpr int N=1005;

vector<int> wolf;
int n,m,l,f;
int honest[N],a[N];

bool dfs(int u)
{
    if(f) return true;
    int len=wolf.size();
    if(len>m) return false;
    if(len==m&&!f)
    {
       int lie_cnt=0;
       for(int i=1;i<=n;i++)
       {
        if(honest[abs(a[i])]*a[i]<0) lie_cnt++;
       }

       if(lie_cnt!=l) return false;

       int lie_wolf=0;
       for(int i=1;i<=n;i++)
        if(honest[i]==-1&&((honest[abs(a[i])]==-1&&a[i]>0)||(honest[abs(a[i])]==1&&a[i]<0))) lie_wolf++;
       if(lie_wolf>=1&&lie_wolf<m)
       { 
           f=1;
        for(int i=0;i<wolf.size();i++)
        {
           
            if(!i) cout<<wolf[i];
            else cout<<' '<<wolf[i];
        }
        return true;
       }
       return false;
    }

    for(int i=u;i>=1;i--)
    {
        honest[i]=-1;
        wolf.eb(i);
        if(dfs(u-1)) return true;
        honest[i]=1;
        wolf.pop_back();
        if(dfs(u-1)) return true;
    }
    return false;
}
void solve()
{
   cin>>n>>m>>l;

   for(int i=1;i<=n;i++) 
   {
    cin>>a[i];
    honest[i]=1;
   }

   if(!dfs(n)) cout<<"No Solution";
}

L1-4 冠军魔术

思路:简单的模拟题,最后输出的0或者1,我们可以用ans=0,在每次推送时异或1,用cnt=0记录推送次数,当cnt为奇数时,变为硬币,为偶数时,牌的数量乘2

void solve()
{
  int n,k;cin>>n>>k;
  int sum=n,ans=0;
  for(int i=1;i<=k;i++)
  {
    if(i&1)
    {
        ans^=1;
    }
    else
    {
        ans^=1;
        sum*=2;
    }
  }
  cout<<ans<<' '<<sum;
}

7-15 谷歌的招聘

思路:最后一题当时没什么时间写了,题目有些细节没看到(有前导零也算是解),导致未能全对,就是一个简单的判断素数,再加一点字符串相关函数的运用。

int judge(int x)
{
    if(x<=1) return 0;
    int f=0;
    for(int i=2;i<=sqrt(x);i++)
        if(x%i==0)
        {
            f=1;
            break;
        }
        if(!f) return 1;
        return 0;
}
void solve()
{
 int l,k;cin>>l>>k;
  string s;cin>>s;
  int f=0;
  for(int i=0;i+k<=l;i++)
  {
    int a=stoi(s.substr(i,k));
    if(judge(a)==1)
    {
       string b=to_string(a);
        if(b.size()!=k)
        {
            for(int j=0;j<k-b.size();j++)
                cout<<0;
        }
        cout<<b;
        f=1;
        break;
    }
  }
  if(f==0) cout<<"404";
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SuperRandi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值