PTA天梯练习赛 刷题笔记2(L2 013-021)

题目库链接:团体程序设计天梯赛-练习集

LAST:PTA天梯练习赛 刷题笔记1

OP

\

L2-013 红色警报 (25 分)

bfs

思路

这道题主要部分就是bfs计数,检验新集合与旧集合是不是元素数少1,只不过需要注意的细节有些多;

我将每一个连通块内编号最小的城市作为该连通块的代表,在该位上存储该块的元素数;

一个城市沦陷后,判断该城所在的连通块元素数量变化情况;
需要注意的是,如果该城即为该连通块的代表,则需要另外选择一个代表对该连通块进行广搜计数;

如果由于城市沦陷导致一些城市失联,则需要对失联的城市重新bfs;

代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<int>rd[502];
int gt[502];//>=0为所属连通块最小号城市编号,-1为未达,-2为被攻占
int c[502];//记录以i为代表的连通块的城市数
void bfs(int x)
{
    c[x]=0;//刷0
    queue<int>que;
    //if(gt[x]==-1)//防呆设计,无必要
    {
        que.push(x);
        gt[x]=x;
        c[x]++;
    }
    while(!que.empty())
    {
        int now=que.front();
        que.pop();
        for(int i:rd[now])
            if(gt[i]==-1)
            {
                que.push(i);
                c[x]++;
                gt[i]=x;
            }
    }
}
int main()
{
    int n,m,a,b,part=0;
    cin>>n>>m;
    while(m--)//存路
    {
        cin>>a>>b;
        rd[a].push_back(b);
        rd[b].push_back(a);
    }
    for(int i=0;i<n;i++)gt[i]=-1;//标记未达
    for(int i=0;i<n;i++)
        if(gt[i]==-1)
        {
            //printf("*");
            bfs(i);
        }
    //初始化完成
    cin>>m;
    int co=m;
    while(m--){
        cin>>a;//a为沦陷城
        b=n;//b为备选代表
        int area=gt[a],car=c[area];//area为沦陷城所在区域,car为该区域城市数
        gt[a]=-2;//标记沦陷
        /*if(car==1)//防呆特判,无必要
        {
            printf("City %d is lost.\n",a);
        }
        else*/
        {
        for(int i=0;i<n;i++)//把该区域内其他城市标记未连接
        {
            if(gt[i]==area&&i!=a)gt[i]=-1;
            if(gt[i]==-1&&i!=a)b=min(b,i);//更新备选城
        }
        if(a==area)area=b;//如有必要,更改代表
        c[area]=0;
        if(area>=0)bfs(area);//如果合法,bfs
        //printf("%d %d %d ",area,c[area],car);
        if(c[area]<car-1)//如果有其余城市失联
        {
            printf("Red Alert: City %d is lost!\n",a);
            for(int i=0;i<n;i++)
                if(gt[i]==-1)
                {
                    bfs(i);//重新bfs
                }
        }
        else printf("City %d is lost.\n",a);
        }
    }
    if(co>=n)printf("Game Over.");//全部沦陷判定
    return 0;
}

L2-014 列车调度 (25 分)

贪心

思路

这个题我常做常忘,这次最后还是参考了这位大佬的题解;

本质是求给定序列最少有多少个递增序列(可不连续);

首先想到的是创建足够多个栈,对于每一个元素,将其压入栈顶元素大于该元素并且栈顶元素最小的栈,如果不存在满足条件的栈,则将其压入新栈;
如此,时间复杂度则到了O(n2),不现实;

在上面的过程中,我们可以发现,对于每个栈,只有栈顶元素是会被用到的,我们便可以只存储栈顶元素;

如果将栈顶元素放入一个set,便可以使用upper_bound十分快速地找到满足条件的set元素(所代表的栈)并将该元素替换为新值;
同理,如果不存在满足条件的set元素,将新值加入set即可;
时间复杂度便被降到了O(nlogn);

因为每个车厢的编号是无重复的,所以不需要考虑误去重的问题;

代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
set<int>st;
int main()
{
    int n,i,g;
    cin>>n;
    for(i=1;i<=n;i++)
    {
        cin>>g;
        auto po=st.upper_bound(g);
        if(po!=st.end())//有满足条件的元素
        {
            st.erase(po);//清除该元素
        }
        st.insert(g);
    }
    cout<<st.size();
    return 0;
}

L2-015 互评成绩 (25 分)

排序?

思路

按要求算出每个同学成绩后排序,按要求输出即可;

注意:题目中要求的“非降序”,不是按输入顺序,而是按升序
所以代码中的pair数组用double数组即可

代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
    int n,k,m;
    cin>>n>>k>>m;
    pair<double,int> sc[10004];
    for(int i=1;i<=n;i++)
    {
        int g[20]={0};
        for(int j=1;j<=k;j++)
        {
            cin>>g[j];
        }
        sort(g+1,g+k+1);
        for(int j=2;j<=k-1;j++)
        {
            sc[i].first+=g[j];
        }
        sc[i].first/=k-2;
        sc[i].second=i;
    }
    sort(sc+1,sc+n+1);
    for(int i=m-1;i>=0;i--)
    printf("%.3lf%s",sc[n-i].first,i?" ":"\0");
    return 0;
}

L2-016 愿天下有情人都是失散多年的兄妹 (25 分)

dfs

思路

遇到这个题首先想到的是并查集,但是每人有父亲与母亲两个上级节点,同时又没有保证只有一个下级节点,所以并查集会非常麻烦;

于是这道题就可以用定深的dfs来解决了;

对第一个人dfs时,将其范围内所有亲友用map标记,对第二个人dfs时,检查范围内有无亲友被标记即可;

这道题的数据非常狠,包括但不限于询问某人的父母与其他同辈是否可婚;
4号测试组的数据怀疑有一些问题,具体描述如下:
默认性别为0时,38行可被注释掉,但是34行不可以,大致想了一下,没想到可能造成这种情况的测试组,也有可能是我想得不够全面,欢迎讨论;

代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

struct 
{
    int s;
    int f[2];
    int fok;
}p[100005];
map<int,int>mp;
bool dfs(int x,int n,int o)
{
    int f=1;
    if(mp[x]){f=0;}
    if(o)mp[x]++;
    if(n<=4&&p[x].fok)
    {
        for(int i=0;i<=1;i++)
            if(p[x].f[i]!=-1)f*=dfs(p[x].f[i],n+1,o);
    }
    return f;
}

int main()
{
    int n,a,b,g;
    char c;
    cin>>n;
    while(n--)
    {
        scanf("%d %c",&g,&c);
        if(c=='M')p[g].s=1;
        else p[g].s=0;
        p[g].fok=1;
        scanf("%d %d",&p[g].f[0],&p[g].f[1]);
        if(p[g].f[0]!=-1)p[p[g].f[0]].s=1;
        if(p[g].f[1]!=-1)p[p[g].f[1]].s=0;
    }
    cin>>n;
    while(n--)
    {
        cin>>a>>b;
        if(p[a].s==p[b].s)
        {
            printf("Never Mind\n");
        }
        else
        {
            mp.clear();
            dfs(a,1,1);
            if(dfs(b,1,0))printf("Yes\n");
            else printf("No\n");
        }
    }
    return 0;
}

L2-017 人以群分 (25 分)

贪心?

思路

首要目的是使两群人人数差尽量小,如果总人数为偶数,则对半分,若总人数为奇数,则两边人数差一;

其次是是两侧分数之和的差尽量大,则按分数排序,一侧取较大的一段,另一侧取较小端即可;
对于总人数为奇数的情况,显然,将中位数分至较大端能使两端分数差更大;

代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int main()
{
    int n,a=0,b=0,g[100005],i;
    cin>>n;
    for(i=1;i<=n;i++)
    scanf("%d",&g[i]);
    sort(g+1,g+1+n);
    if(n&1)
    {
        for(i=1;i<=n/2;i++)
        a+=g[i],b+=g[n+1-i];
        printf("Outgoing #: %d\nIntroverted #: %d\nDiff = %d",n/2+1,n/2,b-a+g[n/2+1]);
    }
    else
    {
        for(i=1;i<=n/2;i++)
        a+=g[i],b+=g[n+1-i];
        printf("Outgoing #: %d\nIntroverted #: %d\nDiff = %d",n/2,n/2,b-a);
    }
    return 0;
}

L2-018 多项式A除以B (25 分)

大除法
有一些大佬的做法还是不太清楚,mark一下,以后看here&here

思路

用数组模拟初中数学的大乘法即可;

百度百科_大除法

补充一点数据范围:
设最高幂次P;
五个样例中,有三个样例满足 P < = 100 P<=100 P<=100
有四个样例满足 P < = 1000 P<=1000 P<=1000
所有样例满足 P < = 10000 P<=10000 P<=10000

代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int main()
{
    double a[10004],b[10004];
    double c[10004]={0};
    int n,g1,m,i,l,ma=-1,mb=-1;
    double g2;
    cin>>n;
    while(n--)cin>>g1>>g2,a[g1]=g2,ma=max(ma,g1);
    cin>>n;
    while(n--)cin>>g1>>g2,b[g1]=g2,mb=max(mb,g1);
    /*for(i=0;i<=ma;i++)printf("%.1lf ",a[i]);
    printf("\n");
    for(i=0;i<=mb;i++)printf("%.1lf ",b[i]);*/
    int top=ma;
    while(top>=mb)
    {
        c[top-mb]=a[top]/b[mb];
        for(i=0;i<=top;i++)
        {
            a[top-i]-=b[mb-i]*c[top-mb];
            if(i==mb)break;
        }
        top--;
    }
    int cc=0,ca=0;
    for(i=0;i<=ma-mb;i++)if(fabs(c[i])>=0.1)cc++;else c[i]=0;
    for(i=0;i<mb;i++)if(fabs(a[i])>=0.1)ca++;else a[i]=0;
    printf("%d",cc);
    if(cc){for(i=ma-mb;i>=0;i--)if(fabs(c[i])>=0.1)printf(" %d %.1lf",i,c[i]);}
    else printf(" 0 0.0");
    printf("\n%d",ca);
    if(ca){for(i=mb;i>=0;i--)if(fabs(a[i])>=0.1)printf(" %d %.1lf",i,a[i]);}
    else printf(" 0 0.0");
    return 0;
}

L2-019 悄悄关注 (25 分)

ZZU视频刷完啦~

思路

分别存下关注列表中的每个人和点赞数,为关注列表中的每个人分配哈希值(并无必要,标记即可),遍历点赞的人,筛选满足条件的输出即可;

代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
# define double long double
const int N = 2e5;
const int M = 20;

map<string,int>hh,ct;
map<string,int>::iterator it;
int main()
{
    int n,chh=1,intg;
    string g;
    cin>>n;
    while(n--)
    {
        cin>>g;
        hh[g]=chh++;
    }
    cin>>n;
    double cal=0;
    for(int i=1;i<=n;i++)
    {
        cin>>g>>intg;
        ct[g]=intg;
        cal+=intg;
    }
    cal/=n;
    int f=0;
    for(it=ct.begin();it!=ct.end();it++)
    {
        if(it->second>=cal&&!hh[it->first])
        {
            cout<<it->first;
            printf("\n");
            f++;
        }
    }
    if(!f)printf("Bing Mei You");
    return 0;
}

L2-020 功夫传人 (25 分)

思路

记录树状关系,进行dfs,统计得道者的武力值即可;

代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5;
const int M = 1e9 + 7;
double k[N + 5];
double val[N + 5];
double dk;
vector<int> rod[N + 5];
void dfs(int x)
{
    val[x] *= k[x];
    for (int i = 0; i < rod[x].size(); i++)
    {
        val[rod[x][i]] = val[x] * dk;
        dfs(rod[x][i]);
    }
}
int main()
{
    int n, m, g;
    double gd;
    cin >> n >> val[0] >> dk;
    dk /= 100.0;
    dk = 1 - dk;
    for (int i = 0; i < n; i++)
    {
        scanf("%d", &m);
        if (m)
        {
            k[i] = 1;
            while (m--)
            {
                scanf("%d", &g);
                rod[i].push_back(g);
            }
        }
        else
        {
            cin >> k[i];
        }
    }
    dfs(0);
    double ans = 0;
    for (int i = 0; i < n; i++)
    {
        // printf("%.2lf\n",val[i]);
        if (k[i] != 1)
            ans += val[i];
    }
    ans = floor(ans);
    printf("%.0lf", ans);
    return 0;
}

L2-021 点赞狂魔 (25 分)

思路

对每个人的点赞标签去重(set / map / 排序去重)后,记录并排序;

按要求输出即可;

代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e3;
const int M = 1e9 + 7;
map<int, int> gt;
string g[102];
pair<pair<int, double>, int> pr[102];
int main()
{
    int n, gint, m, chh = 0;
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> g[i];
        scanf("%d", &m);
        gt.clear();
        for (int j = 1; j <= m; j++)
        {
            cin >> gint;
            gt[gint]++;
        }
        pr[i] = {{gt.size(), m * 1.0 / gt.size()}, i};
    }
    sort(pr + 1, pr + 1 + n,
         [&](pair<pair<int, double>, int> &a, pair<pair<int, double>, int> &b)
         {
             if (a.first.first == b.first.first)
                 return a.first.second < b.first.second;
             else
                 return a.first.first > b.first.first;
         });
    int f = 0;
    for (int i = 1; i <= n && i <= 3; i++)
    {
        if (f)
            printf(" ");
        cout << g[pr[i].second];
        f++;
    }
    while (f < 3)
    {
        printf(" -");
        f++;
    }
    return 0;
}

ED

\

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值