SDUTACM春季集训选拔赛(19级)PTA补题

比赛链接
第一题输出“Hello World!”。
直接进入第二题:
7-2 前世档案 (20 分)
题意:问你在根据字符串最后在图上会走到结论几?
思路:把yn中的n看作二进制中的1,y看作二进制中的0然后求出字符串对应的二进制数加1(因为结论从1开始)即为结果:

#include 
#include 
using namespace std;
char s[120];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    while(m--)
    {
        scanf("%s",s);
        int num=0;
        int id=1;
        for(int i=n-1;i>=0;i--)
        {
            if(s[i]=='n')
            {
                num+=id;
            }
            id*=2;
        }
        printf("%d\n",num+1);
    }
    return 0;
}

第三题:
7-3 古风排版 (20 分)
题意:把一段字符串按照古文排版方式输出出来。
思路:行数给啦需要先求出列数然后按顺序放入二维矩阵;

#include 
#include 
using namespace std;
string s;
char a[120][120];
int main()
{
    int n;
    scanf("%d\n",&n);
    getline(cin,s);
    int len=s.size();
    int li=len/n+(len%n!=0);
    for(int i=0;i<120;i++)
    {
        s+=" ";
    }
    int num=0;
    for(int i=li;i>=1;i--)
    {
        for(int j=1;j<=n;j++)
        {
            a[i][j]=s[num++];
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=li;j++)
        {
            printf("%c",a[j][i]);
        }
        printf("\n");
    }
    return 0;
}

第四题:
7-6 链表去重 (25 分)
题意:给出起点和节点数;给出每个节点的地址,节点中的值和节点指向的下一节点的地址;最后按照绝对值删除绝对值重复节点并将剩下的节点连成一条链表;
首先输出去重后的链表,然后输出被删除的链表。每个结点占一行,按输入的格式输出。
思路:
开一个map用来记录已经出现过的绝对值;然后开一个结构体专门用来存储被删除节点的信息;

#include 
#include 
using namespace std;
map<int ,bool> vis;
struct node
{
    int wei,k;
}a[1100000];
struct noce
{
    int x,y,z;
}b[1100000];
int main()
{
    int qi,n;
    cin>>qi>>n;
    for(int i=0;i<n;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        a[x].k=y;
        a[x].wei=z;
    }
    int cnt=0;
    printf("%05d %d ",qi,a[qi].k);
    vis[abs(a[qi].k)]=1;
    for(int i=a[qi].wei;i!=-1;i=a[i].wei)
    {
        if(vis[abs(a[i].k)])
        {
            b[cnt].x=i;
            b[cnt].y=a[i].k;
            b[cnt++].z=a[i].wei;
            continue;
        }
        printf("%05d\n%05d %d ",i,i,a[i].k);
        vis[abs(a[i].k)]=1;
    }
    printf("-1\n");
    if(cnt==0)return 0;

    printf("%05d %d ",b[0].x,b[0].y);

    for(int i=1;i<cnt;i++)
    {
        printf("%05d\n%05d %d ",b[i].x,b[i].x,b[i].y);
    }
    printf("-1\n");
    return 0;
}

第四道:
7-4 城市间紧急救援 (25 分)
题意:每个节点都有一定的救援队数
求最短路有几条(是分别有多少种)和满足最短路路径中救援队的和;
思路:dijkstra中记录前驱节点信息,然后再开一个cntt数组专门记录和计算该节点到源点的最短路路径条数;最后输出有多少种最短路,注意是有多少种最短路,最短路中路径救援队最大和,和该路径。

#include 
#include 
#include 
using namespace std;
typedef pair<int,int> PII;
const int M = 510*510;
int w[510];
int qian[510];
const int inf = 510*510;
int cnt;
int ne[inf];
int e[M];
int idx;
int zhi[M];
int h[510];
int n,m,s,d;
void add(int u,int v, int w)
{
    e[cnt]=v,zhi[cnt]=w,ne[cnt]=h[u],h[u]=cnt++;
}
int dist[510];
int sum[510];
int vis[510];
int cntt[510];
void dj(int endd)
{
    priority_queue<pair<int,int> > q;
    memset(dist,inf,sizeof(dist));
    memset(vis,0,sizeof(vis));
    memset(sum,0,sizeof(sum));
    dist[endd]=0;
    q.push({0,endd});
    cntt[endd]=1;
    while(q.size())
    {
        int u=q.top().second;
        q.pop();
        if(vis[u])continue;
        vis[u]=1;

        for(int i=h[u];i!=-1;i=ne[i])
        {
            int v=e[i];
            if(vis[v])continue;
            if(dist[v]>dist[u]+zhi[i])
            {
                dist[v]=dist[u]+zhi[i];
                qian[v]=u;
                cntt[v]=cntt[u];
                sum[v]=sum[u]+w[v];
                q.push({-dist[v],v});
            }
            else if(dist[v]==dist[u]+zhi[i])
            {
                cntt[v]+=cntt[u];
                if(sum[v]<sum[u]+w[v])
                {
                    qian[v]=u;
                    sum[v]=sum[u]+w[v];
                    q.push({-dist[v],v});
                }
            }
        }
    }
}
int main()
{

    cin>>n>>m>>s>>d;
    for(int i=0; i<n; i++)
    {
        cin>>w[i];
    }
    memset(h,-1,sizeof(h));
    for(int i=0; i<m; i++)
    {
        int x,y,z;
        cin>>x>>y>>z;
        add(x,y,z);
        add(y,x,z);
    }
    dj(d);
    vector<int> v;
    int start=s;
    while(start!=d)
    {
        v.push_back(start);
        start=qian[start];
    }
    v.push_back(d);
    cout<<cntt[s]<<" "<<sum[s]+w[d]<<endl;
    for(int i=0;i<v.size()-1;i++)
    {
        printf("%d ",v[i]);
    }
    int hou=v.size()-1;
    printf("%d\n",v[hou]);
    return 0;
}

第五题:
7-5 树的遍历 (25 分)
题意:已知一棵二叉树的后序遍历和中序遍历,求层序遍历,(树中各个键值互不相等);
现场状况:刚拿到时大体思路还能记得,关键是struct节点指针在函数中的调用以及最后bfs开队列开啦半天也没开对。(指针最难的不是怎麽指,而是怎麽写才是那末指)
思路:建树的时候按照后续的最后是整棵二叉树的根的原则,找到中序的相同k值的位置,在划分左右为左右子树。最后需要用bfs(队列)来实现层序遍历。

#include 
using namespace std;
int hou[33];
int qian[33];
int zhong[33];
struct node
{
    int k;
    struct node  *l;
    struct node  *r;
};

node *creat(int l1,int r1,int l2,int r2)
{
    node *h;
    if(l1>r1)return NULL;
    h=new node;
    int hoot=hou[r2];
    h->k=hou[r2];
    int id;
    for(int i=l1;i<=r1;i++)
    {
        if(hoot==zhong[i])
        {
            id=i;
            break;
        }
    }
    h->l=creat(l1,id-1,l2,l2+id-1-l1);
    h->r=creat(id+1,r1,r2-r1+id,r2-1);
    return h;
}
int a[55];
int b[55];
int c[55];
void shuchu(node *h)
{
    if(h==NULL)return ;
    printf("%d ",h->k);
    shuchu(h->l);
    shuchu(h->r);
}
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        scanf("%d",&hou[i]);
    }
    for(int i=0;i<n;i++)
    {
        scanf("%d",&zhong[i]);
    }
    struct node *h;
    h=creat(0,n-1,0,n-1);
//    shuchu(h);
    struct node *shu[55];
    shu[0]=h;
    int l=0;
    int r=1;
    vector<int> v;
    while(l<r)
    {
        v.push_back(shu[l]->k);
//        printf("%d\n",shu[l]->k);
        if(shu[l]->l!=NULL)shu[r++]=shu[l]->l;
        if(shu[l]->r!=NULL)shu[r++]=shu[l]->r;
        l++;
    }
    int len=v.size();
    for(int i=0;i<len;i++)
    {
        if(i==len-1)
            printf("%d\n",v[i]);
        else printf("%d ",v[i]);
    }
    return 0;
}

队列用指针的写法:

#include 
using namespace std;
int hou[33];
int qian[33];
int zhong[33];
struct node
{
    int k;
    struct node  *l;
    struct node  *r;
};

node *creat(int l1,int r1,int l2,int r2)
{
    node *h;
    if(l1>r1)return NULL;
    h=new node;
    int hoot=hou[r2];
    h->k=hou[r2];
    int id;
    for(int i=l1;i<=r1;i++)
    {
        if(hoot==zhong[i])
        {
            id=i;
            break;
        }
    }
    h->l=creat(l1,id-1,l2,l2+id-1-l1);
    h->r=creat(id+1,r1,r2-r1+id,r2-1);
    return h;
}
int a[55];
int b[55];
int c[55];
void shuchu(node *h)
{
    if(h==NULL)return ;
    printf("%d ",h->k);
    shuchu(h->l);
    shuchu(h->r);
}
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        scanf("%d",&hou[i]);
    }
    for(int i=0;i<n;i++)
    {
        scanf("%d",&zhong[i]);
    }
    struct node *h;
    h=creat(0,n-1,0,n-1);
//    shuchu(h);
    queue<node *> q;
    q.push(h);
    vector<int> v;
    while(q.size())
    {
        node *root;
        root = q.front();
        q.pop();
        v.push_back(root->k);
//        printf("%d\n",shu[l]->k);
        if(root->l!=NULL)q.push(root->l);
        if(root->r!=NULL)q.push(root->r);
    }
    int len=v.size();
    for(int i=0;i<len;i++)
    {
        if(i==len-1)
            printf("%d\n",v[i]);
        else printf("%d ",v[i]);
    }
    return 0;
}

L2-030 冰岛人 (25 分)
题意:做一个冰岛人两人关系app,
名字的最后‘r’代表冰岛女生,‘n’代表冰岛男生,然后根据男女直接减去名字最后的一段可以得到他的父亲的姓氏。
如果上两种情况都不是:
姓氏的最后一个字母就可以看出此人的性别,然后直接录进此人姓氏的性别;
若两人为异性,且五代以内无公共祖先,则输出 Yes;
若两人为异性,但五代以内(不包括第五代)有 公 共 祖先,则输出 No;
若两人为同性,则输出 Whatever;
若有一人不在名单内,则输出 NA。
思路:(借鉴)被借鉴的博客
开一个第一键值为string,第二键值结构体的map有无此人用map直接就能查,然后是一个人的父亲一定是这个人用自己的名字减去后一小段此处用到啦string的切割,strstr函数。需要快速读入

#include 
using namespace std;

struct people{
    char sex;
    string father;
};
map<string,people> mp;
int panduan(string a,string b)
{
    int i=1,j;
    for(string A = a;!A.empty();A = mp[A].father,i++){
        j=1;
        for(string B = b;!B.empty();B = mp[B].father,j++)
        {
            if(i>=5&&j>=5)
            return 1;
            if(A==B&&(i<5||j<5))
            return 0;
        }
    }
    return 1;
}
int main()
{
    int N;
    ios::sync_with_stdio(false);
    cin>>N;
    string a,b;
    for(int i=0;i<N;i++)
    {
        cin>>a>>b;
        if(b.back()=='r'){
            mp[a]={'f',b.substr(0,b.size()-7)};
        }else if(b.back()=='n'){
            mp[a]={'m',b.substr(0,b.size()-4)};
        }else {
            mp[a].sex=b.back();
        }
    }
    int M;
    cin>>M;
    string str;
    for(int i=0;i<M;i++){
        cin>>a>>str>>b>>str;
        if(mp.find(a)==mp.end()||mp.find(b)==mp.end())
        printf("NA\n");
        else if(mp[a].sex==mp[b].sex)
        printf("Whatever\n");
        else printf("%s\n",panduan(a,b)? "Yes" : "No");
    }
    return 0;
}

第八题:
L3-001 凑零钱 (30 分)
题意:n枚金币,m是需要凑出来的总和,下一行n个数,代表没一枚金币的价值。
输出能凑出m的x枚金币 最小字典序的结果。
9
1 3 5
135
字典序比9小
思路:此题有两种做法,一种是dfs从小往大搜,另一种01背包,dp记录选择。
dfs:这里在dfs之前需要把大于m的金币先筛出去。以减小时间复杂度。

#include 
using namespace std;
int f[10005];
int num=0,n,m;
int flag=0;
int road[10005];
int cnt=0;
void bfs(int r, int sum){
    if(sum>m||flag)return ;
    if(sum==m){
        flag=1;
        for(int i=0;i<cnt;i++)
        {
            printf(i==cnt-1 ? "%d" : "%d ",road[i]);
        }
        cout<<endl;
        return ;
    }
    for(int i=r;i<num;i++){
        road[cnt++]=f[i];
        bfs(i+1,sum+f[i]);
        cnt--;
        if(flag)break;
    }
    return ;
}
int main()
{
    cin>>n>>m;
    int sum=0;
    for(int i=0;i<n;i++){
        int t;
        cin>>t;
        if(t<=m){
            f[num++]=t;
            sum+=t;
        }
    }
    if(sum<m){
        cout<<"No Solution"<<endl;
        return 0;
    }
    sort(f,f+num);
    bfs(0,0);
    if(!flag)
    cout<<"No Solution"<<endl;
    return 0;
}

01背包:注意:从大到小往里放,从小到大往外拿。

#include 
using namespace std;

const int N = 1e4+10;
int f[110];
bool vis[N][110];
int w[N];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&w[i]);
    }
    sort(w+1,w+n+1);
    for(int i=n;i>=1;i--){
        for(int j=m;j>=w[i];j--)
        {
            if(f[j]<=f[j-w[i]]+w[i]){
                f[j]=f[j-w[i]]+w[i];
                vis[i][j]=1;
            }
        }
    }
    if(f[m]==m)
    {
        for(int i=1,j=m;i<=n&&j>0;i++)
        {
            if(vis[i][j]){
                j-=w[i];
                printf("%d",w[i]);
                if(j!=0)printf(" ");
            }
        }
        printf("\n");
    }
    else {
        printf("No Solution\n");
    }
}

第九题:
L3-020 至多删三个字符 (30 分)
题意:给定一个全部由小写英文字母组成的字符串,允许你至多删掉其中 3 个字符,结果可能有多少种不同的字符串?(题目中说的很明白)
思路:
dp[i][j]代表当前ii位中删除j位的方案数目,然后对于i-k长度在三(也可以说是4)以内的如果是s[i]==s[k]则需要进行删除重复的情况。代码中也有简要说明,借鉴代码

#include 
using namespace std;
typedef long long ll;
#define N 2000005
char s[N];
long long dp[N][5];
int main()
{
    scanf("%s",s+1);
    int len=strlen(s+1);
    dp[0][0]=1;
    for(int i=1;i<=len;i++)//从左往右dp
    {
        for(int j=0;j<=3;j++)//枚举删除长度。
        {
            if(i<j)break;//等于是可以的,正好全部删除
            dp[i][j]=dp[i-1][j];//这是第i位的字符不删除的情况
            if(j>=1)dp[i][j]+=dp[i-1][j-1];//选择删除第i位的情况;
            for(int k=i-1;k>=1&&i-k<=j;k--)
            {
                if(s[k]==s[i])
                {
                    dp[i][j]-=dp[k-1][j-(i-k)];
                    //如果两者相等那末之前第k位借用之前的dp会重复计算
                    //长度是i-k+1的字符串上左右两端k位和i位是一样的
                    break;
                }
            }
        }
    }
    ll res=0;
    for(int i=0;i<=3;i++){
        res+=dp[len][i];
    }
    printf("%lld\n",res);
}

你可能感兴趣的:(总结,训练赛)