2024年天梯赛题解

PTA | 程序设计类实验辅助教学平台 (pintia.cn)

L1-097 编程解决一切
L1-098 再进去几个人
L1-099 帮助色盲
L1-100 四项全能
L1-101 别再来这么多猫娘了!
L1-102 兰州牛肉面
L1-103 整数的持续性
L1-104 九宫格

L2-049 鱼与熊掌
L2-050 懂蛇语
L2-051 满树的遍历
L2-052 吉利矩阵

L3-037 夺宝大赛
L3-038 工业园区建设
L3-039 攀岩

L1

1 签到

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
    int x;scanf("%d",&x);return x;
}
int main()
{
    // freopen("1.in","r",stdin);
    // freopen("3.out","w",stdout);

    cout<<"Problem? The Solution: Programming.";
}
1

2 没看懂题,但是猜题意,输出b-a

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
    int x;scanf("%d",&x);return x;
}
int main()
{
    // freopen("1.in","r",stdin);
    // freopen("3.out","w",stdout);

    int a=read(),b=read();
    cout<<b-a;
}
2

3 模拟

第一行:如果a=2或者b=1,输出-,否则如果a=0,输出biii,否则输出dudu

第二行:如果是绿灯,输出move,否则输出stop

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
    int x;scanf("%d",&x);return x;
}
int main()
{
    // freopen("1.in","r",stdin);
    // freopen("3.out","w",stdout);
    int a=read(),b=read();
    if(a==2||b==1)
        printf("-");
    else if(a==0)
        printf("biii");
    else
        printf("dudu");
    puts("");
    if(a==1)
        printf("move");
    else
        printf("stop");
}
3

 4 模拟,数学题

用now表示当前,前i项全能的最少人数,则刚开始now=n,每次循环,拿着a[i]来更新now,公式是now=max(0,now+a-n)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
    int x;scanf("%d",&x);return x;
}
int n,m,now;
int main()
{
    // freopen("1.in","r",stdin);
    // freopen("3.out","w",stdout);
    now=n=read();
    m=read();
    for(int i=1;i<=m;i++)
    {
        int a=read();
        now=max(0,now+a-n);
    }
    cout<<now;
}
4

5 模拟。字符串操作

根据样例5,需要先枚举n个违禁词,再枚举每个位置,进行替换。

判断可以循环或者用substr。替换可以用replace函数。

为了避免"<censored>"的字串是某个违禁词,比如有个违禁词叫cen。可以每次替换后写一个i=i+10;并且替换时使用"\1\1\1\1\1\1\1\1\1\1"这个串而非"<censored>",避免违禁词再次被替换。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
    int x;scanf("%d",&x);return x;
}
string cen="<censored>",s[110],t,tt="\1\1\1\1\1\1\1\1\1\1";
int n,ans;
void work(int &i,int j)
{
    if(i+s[j].size()>t.size()){
        i++;
        return ;
    }
    for(int k=0;k<s[j].size();k++)
    {
        if(t[i+k]!=s[j][k]){
            i++;
            return ;
        }
    }
    ans++;
    t.replace(i,s[j].size(),tt);
    i=i+10;
}
int main()
{
    // freopen("1.in","r",stdin);
    // freopen("3.out","w",stdout);
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>s[i];
    int k;
    cin>>k;
    getline(cin,t);
    getline(cin,t);
    for(int j=1;j<=n;j++)
        for(int i=0;i<t.size();)
            work(i,j);
    if(ans<k)
    {
        for(int i=0;i<t.size();i++)
            if(t[i]=='\1')
                t.replace(i,cen.size(),cen);
        cout<<t;
    }
    else
        cout<<ans<<"\nHe Xie Ni Quan Jia!";
}
5

 6 模拟,统计

用一个sum存买了几个,ans存多少钱即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
    int x;scanf("%d",&x);return x;
}
int n,sum[110],x,y;
double ans,a[110];
int main()
{
    // freopen("1.in","r",stdin);
    // freopen("3.out","w",stdout);
    n=read();
    for(int i=1;i<=n;i++)
        scanf("%lf",&a[i]);
    while(scanf("%d%d",&x,&y)!=-1)
    {
        sum[x]+=y;
        ans=ans+y*a[x];
    }
    for(int i=1;i<=n;i++)
        cout<<sum[i]<<'\n';
    printf("%.2lf",ans);
}
6

7 搜索,数学,模拟

考虑先写一个dfs询问每个数字的持续性。然后用map套vector存所有持续性的数字。则答案是map里first最大的那些数字,输出一下。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
    int x;scanf("%d",&x);return x;
}
int ask(int x)
{
    int t=1;
    do
    {
        t=t*(x%10);
        x=x/10;
    }while(x);
    return t;
}
int dfs(int x)
{
    if(ask(x)==x)
        return 0;
    return dfs(ask(x))+1;
}
int a,b;
map<int,vector<int>>o;
int main()
{
    // freopen("1.in","r",stdin);
    // freopen("3.out","w",stdout);
    a=read();b=read();
    for(int i=a;i<=b;i++)
        o[dfs(i)].push_back(i);
    cout<<o.rbegin()->first<<'\n';
    auto x=o.rbegin()->second;
    cout<<x[0];
    for(int i=1;i<x.size();i++)
        cout<<' '<<x[i];
}
7

8 模拟

考虑用一个check(set<int>a)来判断1~9是否恰好在set里出现一次,剩下的任务就是枚举每行每列9个3*3即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
    int x;scanf("%d",&x);return x;
}
int v[10][10];
int check(set<int>a)
{
    for(int i=1;i<=9;i++)
        if(a.count(i)==0)
            return 0;
    return 1;
}
int check(int x)
{
    set<int>a,b;
    for(int i=1;i<=9;i++)
        a.insert(v[x][i]),b.insert(v[i][x]);    
    return check(a)&check(b);
}
int check(int x,int y)
{
    set<int>a;
    for(int i=0;i<3;i++)
        for(int j=0;j<3;j++)
            a.insert(v[i+x][j+y]);
    return check(a);
}
void work()
{
    for(int i=1;i<=9;i++)
        for(int j=1;j<=9;j++)
            v[i][j]=read();
    for(int i=1;i<=9;i++)
        if(check(i)==0)
        {
            cout<<"0\n";
            return ;
        }
    for(int i=1;i<=9;i+=3)
        for(int j=1;j<=9;j+=3)
            if(check(i,j)==0)
            {
                cout<<"0\n";
                return ;
            }
    cout<<"1\n";
}
int main()
{
    // freopen("1.in","r",stdin);
    // freopen("3.out","w",stdout);
    for(int t=read();t;t--)
        work();
}
8

 L2

1 mao套bitset

为了解决兼得,我一眼想到了bitset。f[x]表示对于物品x,n个人的拥有情况。写好后交了一发,发现数组开小了,开大点却空间炸了。

仔细一看,q只有100,所以有用的bitset最多只有200个,考虑整一个flag,把原来的bitset<100010>f[100010]改成map<int,bitset<100010>>f即可

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
    int x;scanf("%d",&x);return x;
}
int n,m,q;
int flag[100010],x[110],y[110];
map<int,bitset<100010>>f;
vector<int>a[100010];
int main()
{
    // freopen("1.in","r",stdin);
    // freopen("3.out","w",stdout);
    n=read();m=read();
    for(int i=1;i<=n;i++)
        for(int t=read();t;t--)
            a[i].push_back(read());
    q=read();
    for(int i=1;i<=q;i++)
    {
        x[i]=read();y[i]=read();
        flag[x[i]]=flag[y[i]]=1;
    }
    for(int i=1;i<=n;i++)
        for(auto v:a[i])
            if(flag[v])
                f[v][i]=1;
    for(int i=1;i<=q;i++)
        cout<<(f[x[i]]&f[y[i]]).count()<<'\n';
}
1

2 map套vector

首先实现一个把s的首字母提取出来得到t的函数

然后可以发现,可以用map套vector存某个前缀t的所有句子。读入时先存进去,输出时去找对应的map即可

为了解决格式错误,需要特判提取s首字母的时候,如果s开头是空格,则不能把开头放进去。

为了解决字典序,可以遍历一下map,把vector排个序。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
    int x;scanf("%d",&x);return x;
}
string s,t;
int n,q;
map<string,vector<string>>o;
void work()
{
    t.clear();
    if(s[0]!=' ')
        t=t+s[0];
    for(int i=1;i<s.size();i++)
        if(s[i-1]==' '&&s[i]!=' ')
            t=t+s[i];
}
int main()
{
    cin>>n;
    getline(cin,s);
    for(int i=1;i<=n;i++)
    {
        getline(cin,s);
        work();
        o[t].push_back(s);
    }
    for(auto &x:o)
        sort(x.second.begin(),x.second.end());
    cin>>q;
    getline(cin,s);
    for(int i=1;i<=q;i++)
    {
        getline(cin,s);
        work();
        if(o.count(t)==0)
            cout<<s<<'\n';
        else
        {
            cout<<o[t][0];
            for(int i=1;i<o[t].size();i++)
                cout<<'|'<<o[t][i];
            cout<<'\n';
        }
    }
}
2

3 dfs

最喜欢的建图方式,直接e[fa].push_back(i)即可,dfs的时候也不需要检查fa

考虑建图,存边,得到最大的度数。

第一问输出最大的度数

第二问判断是否所有的度数=0或者等于最大的度数

第三问输出dfs序即可,为了字典序,需要跑之前sort一下边

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
    int x;scanf("%d",&x);return x;
}
int root,n,ans;
vector<int>e[100010];
void dfs(int x)
{
    if(x==root)
        cout<<x;
    else
        cout<<' '<<x;
    sort(e[x].begin(),e[x].end());
    for(auto y:e[x])
        dfs(y);
}
int main()
{
    // freopen("1.in","r",stdin);
    // freopen("3.out","w",stdout);
    n=read();
    for(int i=1;i<=n;i++)
    {
        int x=read();
        if(x==0)
            root=i;
        else
            e[x].push_back(i);
    }
    for(int i=1;i<=n;i++)
        ans=max(ans,(int)e[i].size());
    cout<<ans<<' ';
    for(int i=1;i<=n;i++)
        if(ans!=(int)e[i].size()&&0!=e[i].size())
            ans=-1;
    if(ans==-1)
        cout<<"no\n";
    else
        cout<<"yes\n";
    dfs(root);
}
3

4 动态规划

考虑f[i][x1][x2]表示当前在第i行,第1列和为x1,第2列和为x2的方案数,转移一下即可。

x1+x2需要等于i*l才有意义,所以可以省去一维,但是空间开的下也就不管他了。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
    int x;scanf("%d",&x);return x;
}
int n,l;
int f[3][60][60],g[4][60][60][60],h[5][60][60][60][60];
int main()
{
    // freopen("1.in","r",stdin);
    // freopen("3.out","w",stdout);
    l=read();n=read();
    if(n==2)
    {
        f[0][0][0]=1;
        for(int i=1;i<=n;i++)
        {
            for(int x1=0;x1<=l;x1++)
            {
                int x2=l-x1;
                for(int y1=0;y1<=(i-1)*l;y1++)
                {
                    int y2=(i-1)*l-y1;
                    f[i][x1+y1][x2+y2]=f[i][x1+y1][x2+y2]+f[i-1][y1][y2];
                }
            }
        }
        cout<<f[n][l][l];
    }
    else if(n==3)
    {

        g[0][0][0][0]=1;
        for(int i=1;i<=n;i++)
        {
        for(int x1=0;x1<=l;x1++)
        {
        for(int x2=0;x2<=l;x2++)
        {
                int x3=l-x1-x2;
                if(x3<0)break;
                for(int y1=0;y1<=(i-1)*l;y1++)
                {
                for(int y2=0;y2<=(i-1)*l;y2++)
                {
                    int y3=(i-1)*l-y1-y2;
                    if(y3<0)break;
                    g[i][x1+y1][x2+y2][x3+y3]=g[i][x1+y1][x2+y2][x3+y3]+g[i-1][y1][y2][y3];
                }
                }
        }
        }
        }
        cout<<g[n][l][l][l];
    }
    else if(n==4)
    {

        h[0][0][0][0][0]=1;
        for(int i=1;i<=n;i++)
        {
        for(int x1=0;x1<=l;x1++)
        {
        for(int x2=0;x2<=l;x2++)
        {
        for(int x3=0;x3<=l;x3++)
        {
                int x4=l-x1-x2-x3;
                if(x4<0)break;
                for(int y1=0;y1<=(i-1)*l;y1++)
                {
                for(int y2=0;y2<=(i-1)*l;y2++)
                {
                for(int y3=0;y3<=(i-1)*l;y3++)
                {
                    int y4=(i-1)*l-y1-y2-y3;
                    if(y4<0)break;
                    h[i][x1+y1][x2+y2][x3+y3][x4+y4]=h[i][x1+y1][x2+y2][x3+y3][x4+y4]+h[i-1][y1][y2][y3][y4];
                }
                }
                }
        }
        }
        }
        }
        cout<<h[n][l][l][l][l];
    }
}
4

 本题也可搜索+剪枝或者搜索+打表解决。

L3

1 bfs最短路,排序

首先写一个从大本营的最短路

则一个队伍火拼失去资格,当然是存在多个队伍最短路和他相等。于是可以用sort结构体排序一下,判断左边右边是否和他最短路相等。

如果最短路=0x3f3f3f3f也要失去资格,可以给k+1赋值最短路为0x3f3f3f3f来解决,这样所有的0x3f3f3f3f也会被判失去资格(continue)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
    int x;scanf("%d",&x);return x;
}
int n,m,k,x,y,nx,ny,d[110][110],a[110][110];
int dx[]={1,-1,0,0},dy[]={0,0,1,-1};
queue<pair<int,int>>q;
struct node
{
    int i,t;
    friend bool operator <(node a,node b)
    {
        return a.t<b.t;
    }
}o[10010];
int main()
{
    // freopen("1.in","r",stdin);
    // freopen("3.out","w",stdout);
    n=read();m=read();
    memset(d,0x3f,sizeof(d));
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            a[i][j]=read();
            if(a[i][j]==2)
                x=i,y=j;
        }
    q.push({x,y});
    d[x][y]=0;
    while(q.size())
    {
        x=q.front().first;
        y=q.front().second;
        q.pop();
        for(int i=0;i<4;i++)
        {
            nx=x+dx[i];
            ny=y+dy[i];
            if(nx==0||ny==0||nx==n+1||ny==m+1||d[nx][ny]<=d[x][y]+1||a[nx][ny]==0)
                continue;
            d[nx][ny]=d[x][y]+1;
            q.push({nx,ny});
        }
    }
    k=read();
    for(int i=1;i<=k;i++)
    {
        y=read(),x=read();
        o[i].i=i;
        o[i].t=d[x][y];
    }
    sort(o+1,o+1+k);
    o[1+k].t=0x3f3f3f3f;
    for(int i=1;i<=k;i++)
    {
        if(o[i-1].t==o[i].t||o[i+1].t==o[i].t||o[i].t==0x3f3f3f3f)
            continue;
        cout<<o[i].i<<' '<<o[i].t;
        return 0;
    }
    cout<<"No winner.";
}
1

 

 

2 二分,前缀和

拿到题一眼二分,但是写了会发现不好实现,于是决定先写一个暴力。

考虑使用一个ti往外枚举,贪心地选取。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
    int x;scanf("%d",&x);return x;
}
int n,m,k;
char s[500010];
ll ask(int x)
{
    ll tsum=0;
    int tk=k,tm=m,ti=1,i;
    if(s[x]=='1')
        tk--;
    else if(tm)
        tk--,tm--;
    for(;ti<=2*n&&tm&&tk;ti++)
    {
        if(ti&1)
        {
            i=ti/2+1;
            if(x-i<1)continue;
            if(s[x-i]=='1')
                tk--,tsum+=i;
            else 
                tk--,tm--,tsum+=i;
        }
        else
        {
            i=ti/2;
            if(x+i>n)continue;
            if(s[x+i]=='1')
                tk--,tsum+=i;
            else
                tk--,tm--,tsum+=i;
        }
    }
    for(;ti<=2*n&&tk;ti++)
    {
        if(ti&1)
        {
            i=ti/2+1;
            if(x-i<1)continue;
            if(s[x-i]=='1')
                tk--,tsum+=i;
        }
        else
        {
            i=ti/2;
            if(x+i>n)continue;
            if(s[x+i]=='1')
                tk--,tsum+=i;
        }
    }
    return tsum;
}
void work()
{
    n=read();m=read();k=read();
    scanf("%s",s+1);
    cout<<ask(1);
    for(int i=2;i<=n;i++)
        cout<<' '<<ask(i);
    cout<<'\n';
}
int main()
{
    // freopen("1.in","r",stdin);
    // freopen("3.out","w",stdout);
    for(int t=read();t;t--)
        work();
}
L3-2 暴力

然后我拿了19分,看不出问题就去写前面的分了。写完前面的分回来又想了想拿了21。

再想一想,可以发现这个ti可以拿来二分。首先输出暴力代码中的每个ti。

 然后开始修改代码,使得二分出来的和他的一样。

最后算一下贡献。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
    int x;scanf("%d",&x);return x;
}
int n,m,k,sum1[200010],sum0[200010];
ll sum[200010];
char s[200010];
void ask(int x,int ti,int &l,int &r)
{
    if(ti&1)
        l=max(1,x-ti/2-1),r=min(n,x+ti/2);
    else
        l=max(1,x-ti/2),r=min(n,x+ti/2);
}
ll ask(ll x)
{
    int tk=k,tm=m,ti,i;
    if(s[x]=='1')
        tk--;
    else  if(tm)
        tk--,tm--;
    int L=0,R=2*n,MID,l,r;
    while(L+1<R)
    {
        MID=(L+R)/2;
        ask(x,MID,l,r);
        if(sum0[r]-sum0[l-1]-(s[x]=='0')>=tm||r-l>=tk)
            R=MID;
        else
            L=MID;
    }
    ask(x,L,l,r);
    if(sum0[r]-sum0[l-1]-(s[x]=='0')<tm)
        L=R;
    R=2*n;
    int l0,r0;
    ask(x,L,l0,r0);
    ll tsum=(1ll+x-l0)*(x-l0)/2+(1ll+r0-x)*(r0-x)/2;
    tk=tk-(r0-l0);
    while(L+1<R)
    {
        MID=(L+R)/2;
        ask(x,MID,l,r);
        if(sum1[r]-sum1[r0]+sum1[l0-1]-sum1[l-1]>=tk)
            R=MID;
        else
            L=MID;
    }
    ask(x,L,l,r);
    if(sum1[r]-sum1[r0]+sum1[l0-1]-sum1[l-1]<tk)
        ask(x,R,l,r);
    return tsum+(sum[r]-sum[r0])-(sum1[r]-sum1[r0])*x+x*(sum1[l0-1]-sum1[l-1])-sum[l0-1]+sum[l-1];
}
void work()
{
    n=read();m=read();k=read();
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)
    {
        sum1[i]=sum1[i-1]+(s[i]=='1');
        sum0[i]=sum0[i-1]+(s[i]=='0');
        sum[i]=sum[i-1]+(s[i]=='1')*i;
    }
    cout<<ask(1);
    for(int i=2;i<=n;i++)
        cout<<' '<<ask(i);
    cout<<'\n';
}
int main()
{
    // freopen("1.in","r",stdin);
    // freopen("1.out","w",stdout);
    for(int t=read();t;t--)
        work();
}
L3-2

 

posted @ 2024-04-21 11:29  zzuqy  阅读(305)  评论(1编辑  收藏  举报