PTA天梯训练赛一&二

训练赛一

7-5 连续因子 (20 分)

思路:
暴力枚举起点,每次从起点往后延伸,并且更新长度和起点,最后输出即可。
代码:

int main(){
    ll n;scanf("%lld",&n);
    ll res=0,pos=-1;
    for(ll i=2;i*i<=n+1;i++){
        ll tmp=0;
        ll tmpn=n,t=i;
        for(;tmpn%t==0;t++,tmp++)
            tmpn/=t;
        if(res<tmp){
            res=tmp;
            pos=i;
        }
    }
    if(res==0){
        cout<<"1"<<"\n"<<n<<"\n";
        return 0;
    }
    cout<<res<<endl;
    for(int i=1;i<=res;i++){
        cout<<pos+i-1;
        if(i!=res) cout<<"*";
    }
    puts("");
    return 0;
}

7-6 链表去重 (25 分)

思路:
之前存图的时候用链式前向星,这里也可以借助数组模拟链表,然而我写的暴力只过了15分。
代码:

const int maxn=1e5+100;
bool vis[maxn];
int w[maxn],ne[maxn],del_w[maxn],del_ne[maxn];
int n,head=-1,pre=-1,del_head=-1,del_tail=-1;

int main(){
    head=read,n=read;
    for(int i=1;i<=n;i++){
        int pos=read;
        w[pos]=read;ne[pos]=read;
    }
    for(int i=head;~i;i=ne[i]){
        if(vis[abs(w[i])]){
            if(pre==-1) head=ne[i];
            else ne[pre]=ne[i];
            if(del_head==-1) del_head=i;
            del_w[i]=w[i];
            if(del_tail!=-1) del_ne[del_tail]=i;
            del_tail=i;
        }
        else{
            vis[abs(w[i])]=1;
            pre=i;
        }
    }
    del_ne[del_tail]=-1;
    for(int i=head;~i;i=ne[i]){
        printf("%05d %d ",i,w[i]);
        if(ne[i]==-1) puts("-1");
        else printf("%05d\n",ne[i]);
    }
    for(int i=del_head;~i;i=del_ne[i]){
        printf("%05d %d ",i,del_w[i]);
        if(del_ne[i]==-1) puts("-1");
        else printf("%05d\n",del_ne[i]);
    }
    return 0;
}

7-8 凑零钱 (30 分)

思路:
背包dp判断能否构成的同时记录路径,输出答案的时候倒着输出。
dfs也能过。
代码:

const int maxn=1e4+100;
int n,m,a[maxn],dp[maxn];
int vis[maxn][maxn];
bool cmp(int a,int b){
    return a>b;
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i];
    sort(a+1,a+1+n,cmp);
    for(int i=1;i<=n;i++)
        for(int j=m;j>=0;j--){
            if(j>=a[i]){
                if(dp[j]<=dp[j-a[i]]+a[i]){
                    dp[j]=dp[j-a[i]]+a[i];
                    vis[i][j]=1;
                }
            }
        }
    if(dp[m]!=m) puts("No Solution");
    else{
        int tmpm=m;
        while(tmpm){
            if(vis[n][tmpm]){
                cout<<a[n];
                tmpm-=a[n];
                if(tmpm>0) cout<<" ";
            }
            n--;
        }
    }
    return 0;
}

7-9 特殊堆栈 (30 分)

思路:
有点思维的题。在动态求中位数时,一般都是用对顶堆维护。但是对顶堆的删除操作不容易实现。
先考虑暴力的做法,是维护一个栈和一个vector容器,每次询问时都对vector进行sort,这样复杂度是 O ( n 2 ) O(n^{2}) O(n2)
如果在插入的时候就保持vector的有序,每次查询时都二分查找该数在有序列表中的位置,时间复杂度就降到了 O ( n l o g n + c ) O(nlogn+c) O(nlogn+c)
代码:

const int maxn=1e5+100;
stack<int>stk;
vector<int>v;
int main(){
    int n;cin>>n;
    char op[15];
    for(int i=1;i<=n;i++){
        cin>>op;
        ///cout<<op<<endl;
        if(op[1]=='o'){
            if(v.size()==0) puts("Invalid");
            else{
                int x=stk.top();
                stk.pop();
                cout<<x<<endl;
                auto pos=lower_bound(v.begin(),v.end(),x);
                v.erase(pos);
            }
        }
        else if(op[1]=='u'){
            int x;cin>>x;
            stk.push(x);
            auto pos=lower_bound(v.begin(),v.end(),x);
            v.insert(pos,x);
        }
        else if(op[1]=='e') {
            if(v.size()==0) puts("Invalid");
            else{
                int t=(v.size()+1)/2-1;
                cout<<v[t]<<endl;
            }
        }
        cout<<i<<" "<<op<<endl;
    }
    return 0;
}

7-11 肿瘤诊断 (30 分)

思路:
三维的bfs,有点离谱,注意判断每个连通块的最低1的个数。
代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct node{
    int x,y,z;
};
int a[60][1300][130],n,m,l,t,b[60][1300][130],res=0;
bool check(int x,int y,int z){
    if(x>=0&&x<l&&y>=0&&y<m&&z>=0&&z<n&&a[x][y][z]==1&&b[x][y][z]==0)
        return 1;
    return 0;
}
int nx[]={1,-1,0,0,0,0};
int ny[]={0,0,1,-1,0,0};
int nz[]={0,0,0,0,1,-1};
void bfs(int x,int y,int z){
    queue<node>q;
    q.push({x,y,z});
    b[x][y][z]=1;
    int tmp=0;
    while(!q.empty()){
        node t=q.front();q.pop();
        tmp++;
        int tx=t.x,ty=t.y,tz=t.z;
        for(int i=0;i<6;i++){
            int xx=tx+nx[i],yy=ty+ny[i],zz=tz+nz[i];
            if(check(xx,yy,zz)){
                b[xx][yy][zz]=1;
                q.push({xx,yy,zz});
            }
        }
    }
    if(tmp>=t) res+=tmp;
}
int main(){
    cin>>m>>n>>l>>t;
    for(int i=0;i<l;i++)
        for(int j=0;j<m;j++)
            for(int k=0;k<n;k++)
                cin>>a[i][j][k];
    for(int i=0;i<l;i++)
        for(int j=0;j<m;j++)
            for(int k=0;k<n;k++)
                if(a[i][j][k]==1&&!b[i][j][k])
                    bfs(i,j,k);
    cout<<res<<endl;
    return 0;
}

训练赛二

7-4 天梯地图 (30 分)

思路:
跑两次最短路。
对于时间来说,时间最短为第一关键字,距离最短为第二关键字。
对于距离来说,距离最短为第一关键字,节点最少为第二关键字。
用pre数组记录一下这个点是从哪个点转移过来的,输出的时候倒着推回去。
判断两个路径是否相同,先判断节点个数,在枚举节点判断对应的是否相等。
n只有500,写个朴素的dijkstra就好了
也不知道被卡哪了。
代码:

const int maxn=1e6+7,inf=0x3f3f3f3f;
int n,m,g1[510][510],g2[510][510],s,e;
int dis1[maxn],st1[maxn],pre1[maxn],cnt[maxn];
int dis2[maxn],st2[maxn],pre2[maxn],d[maxn];
void dijkstra1(){
    memset(dis1,0x3f,sizeof dis1);
    dis1[s]=0;
    pre1[s]=-1;
    for(int i=1;i<=n;i++) cnt[i]=1;
    for(int i=0;i<n-1;i++){
        int t=-1;
        for(int j=1;j<=n;j++)
            if(!st1[j]&&(t==-1||dis1[t]>dis1[j]))
                t=j;
        for(int j=1;j<=n;j++){
            if(dis1[j]>dis1[t]+g1[t][j]){
                dis1[j]=dis1[t]+g1[t][j];
                pre1[j]=t;
                cnt[j]=cnt[t]+1;
            }
            else if(dis1[j]==dis1[t]+g1[t][j]){
                if(cnt[j]>cnt[t]+1){
                    pre1[j]=t;
                    cnt[j]=cnt[t]+1;
                }
            }
        }
        st1[t]=1;
    }
}
void dijkstra2(){
    memset(dis2,0x3f,sizeof dis2);
    memset(d,0x3f,sizeof d);
    dis2[s]=0;d[s]=0;
    pre2[s]=-1;
    for(int i=0;i<n-1;i++){
        int t=-1;
        for(int j=1;j<=n;j++)
            if(!st2[j]&&(t==-1||dis2[t]>dis2[j]))
                t=j;
        for(int j=1;j<=n;j++){
            if(dis2[j]>dis2[t]+g2[t][j]){
                dis2[j]=dis2[t]+g2[t][j];
                pre2[j]=t;
                d[j]=d[t]+g1[t][j];
            }
            else if(dis2[j]==dis2[t]+g2[t][j]){
                if(d[j]>d[t]+g1[t][j]){
                    pre1[j]=t;
                    d[j]=d[t]+g1[t][j];
                }
            }
        }
        st2[t]=1;
    }
}
int main(){
    n=read,m=read;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(i==j) g1[i][j]=g2[i][j]=0;
            else g1[i][j]=g2[i][j]=inf;
    rep(i,1,m){
        int u=read,v=read,op=read,l=read,t=read;
        u++,v++;
        if(op==1){
            g1[u][v]=min(g1[u][v],l);
            g2[u][v]=min(g2[u][v],t);
        }
        else{
            g1[u][v]=min(g1[u][v],l);
            g2[u][v]=min(g2[u][v],t);
            g1[v][u]=min(g1[v][u],l);
            g2[v][u]=min(g2[v][u],t);
        }
    }
    s=read,e=read;
    s++,e++;
    dijkstra1();
    dijkstra2();
    int res_time=dis2[e],res_dis=dis1[e];
    string path_time,path_dis;
    int tmp=e;
    while(tmp!=-1){
        path_time+=(tmp-1+'0');
        tmp=pre2[tmp];
    }
    tmp=e;
    while(tmp!=-1){
        path_dis+=(tmp-1+'0');
        tmp=pre1[tmp];
    }
    reverse(path_dis.begin(), path_dis.end());
    reverse(path_time.begin(), path_time.end());
    if(path_dis==path_time){
        printf("Time = %d; Distance = %d: ",res_time,res_dis);
        for(int i=0;i<path_dis.size();i++){
            cout<<path_dis[i];
            if(i!=path_dis.size()-1) cout<<" => ";
        }
    }
    else{
        printf("Time = %d: ",res_time);
        for(int i=0;i<path_time.size();i++){
            cout<<path_time[i];
            if(i!=path_time.size()-1) cout<<" => ";
        }
        puts("");
        printf("Distance = %d: ",res_dis);
        for(int i=0;i<path_dis.size();i++){
            cout<<path_dis[i];
            if(i!=path_dis.size()-1) cout<<" => ";
        }
    }
    return 0;
}

7-9 家庭房产 (25 分)

思路:
思路不难想,就是并查集,但是有点难处理。
每次将根节点维护为家族里编号最小的编号,剩下的就是模拟了。
代码:

const int maxn=11000;
int root[maxn],siz[maxn];
struct node{
    ll minid,siz,id;
    double cnt,area;
}a[1100];
int cnt_sum[maxn],cnt_area[maxn];
bool vis[maxn];
int Find(int x){
    if(x!=root[x]) root[x]=Find(root[x]);
    return root[x];
}
void Union(int x,int y){
    x=Find(x),y=Find(y);
    if(x!=y){
        if(x<y){
            root[y]=x;
            siz[x]+=siz[y];
            cnt_area[x]+=cnt_area[y];
            cnt_sum[x]+=cnt_sum[y];
        }
        else{///y的编号小
            root[x]=y;
            siz[y]+=siz[x];
            cnt_area[y]+=cnt_area[x];
            cnt_sum[y]+=cnt_sum[x];
        }
    }
}
bool cmp(node a,node b){
    if(a.area==b.area){
        return a.id<b.id;
    }
    return a.area>b.area;

}
int main(){
    for(int i=0;i<=9999;i++) root[i]=i,siz[i]=1;
    int n=read;
    rep(i,1,n){
        int now=read;
        vis[now]=1;
        int fa=read,mo=read;
        if(fa!=-1){
            Union(fa,now);
            vis[fa]=1;
        }
        if(mo!=-1){
            vis[mo]=1;
            Union(mo,now);
        }
        int k=read;
        rep(j,1,k){
            int son=read;
            vis[son]=1;
            Union(son,now);
        }
        int t=Find(now);
        ll t1=read,t2=read;
        cnt_sum[t]+=t1;
        cnt_area[t]+=t2;
    }
    int cnt=0;
    for(int i=0;i<=9999;i++)
        if(vis[i]&&i==Find(i)){
            a[++cnt]={i,siz[i],cnt,cnt_sum[i]*1.0/siz[i],cnt_area[i]*1.0/siz[i]};
        }
    sort(a+1,a+1+cnt,cmp);
    cout<<cnt<<endl;
    for(int i=1;i<=cnt;i++)
        printf("%04lld %lld %.3f %.3f\n",a[i].minid,a[i].siz,a[i].cnt,a[i].area);
	return 0;
}

7-10 最长对称子串 (25 分)

思路:
有好几种解法:
1.马拉车,复杂度 O ( n ) O(n) O(n)
2.二分对称子串的长度,复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
3.枚举对称点,每次从对称点往周围延伸,记录最大长度。跟马拉车的思想类似,复杂度 O ( n 2 ) O(n^{2}) O(n2)
4.枚举起点和终点,复杂度 O ( n 3 ) O(n^{3}) O(n3),但是跑不满,应该也能过。
代码:

const int maxn= 3000+10;
char str[maxn];
string s;
int len1,len2,res,p[maxn];
void init(){
    str[0]='$';str[1]='#';
    for(int i=0;i<len1;i++){
        str[i*2+2]=s[i];
        str[i*2+3]='#';
    }
    len2=len1*2+2;
    str[len2]='*';
}
void manacher(){
    int id=0,mx=0;
    for(int i=1;i<len2;i++){
        if(mx>i) p[i]=min(p[id*2-i],mx-i);
        else p[i]=1;
        for(;str[i+p[i]]==str[i-p[i]];p[i]++);
        if(p[i]+i>mx){
            mx=p[i]+i;
            id=i;
        }
    }
}
int main(){
	getline(cin,s);
	len1=s.size();
	init();
	manacher();
	int res=0;
	for(int i=1;i<len2;i++)
        res=max(res,p[i]);
    cout<<res-1;
	return 0;
}


7-11 垃圾箱分布 (30 分)

思路:
将垃圾箱的编号调整到 [ n + 1 , n + m ] [n+1,n+m] [n+1,n+m]
m最大为10,对每个垃圾箱都跑一遍最短路,记录最短距离,平均距离,编号,sort后输出。
代码:

const int maxn=1e6+7,inf=0x3f3f3f3f;
int n,m,k,ds,g[1100][1100];
struct node{
    int id,minn;
    double ave;
}a[12];
int idx;
bool cmp(node a,node b){
    if(a.minn==b.minn){
        if(a.ave==b.ave){
            return a.id<b.id;
        }
        return a.ave<b.ave;
    }
    return a.minn>b.minn;
}
int dis[maxn];///记录当前点到起点的距离
bool st[maxn];///若当前点的最短距离已经确定 为true

void dfs(int s){
    memset(dis,0x3f,sizeof dis);///初始化距离为正无穷
    memset(st,0,sizeof st);
    dis[s]=0;///从1开始更新
    for(int i=0;i<n+m-1;i++){
        int t=-1;
        ///找到当前不在st中而且距离最小的点t
        for(int j=1;j<=n+m;j++)
            if(!st[j]&&(t==-1||dis[t]>dis[j]))
                t=j;
        ///用点t更新其他不在st中的点的距离
        for(int j=1;j<=n+m;j++)
            dis[j]=min(dis[j],dis[t]+g[t][j]);
        ///将t点放到st集合里
        st[t]=true;
    }
    int res=inf,cnt=0,sum=0;
    for(int i=1;i<=n;i++){
        if(dis[i]>ds){
            break;
        }
        sum+=dis[i];cnt++;
        res=min(res,dis[i]);
    }
    if(cnt==n){
        idx++;
        a[idx]={s,res,sum*1.0/n};
    }

}

int main(){
    n=read,m=read,k=read,ds=read;
    for(int i=1;i<=n+m;i++)
        for(int j=1;j<=n+m;j++)
            if(i==j) g[i][j]=0;
            else g[i][j]=inf;
    for(int i=1;i<=k;i++){
        string x,y;cin>>x>>y;
        int t=read;
        int cntx=0,cnty=0;
        if(x[0]=='G'){
            for(int j=1;j<x.size();j++)
                cntx=cntx*10+x[j]-'0';
            cntx+=n;
        }
        else{
            for(int j=0;j<x.size();j++)
                cntx=cntx*10+x[j]-'0';
        }
        if(y[0]=='G'){
            for(int j=1;j<y.size();j++)
                cnty=cnty*10+y[j]-'0';
            cnty+=n;
        }
        else{
            for(int j=0;j<y.size();j++)
                cnty=cnty*10+y[j]-'0';
        }
       /// cout<<i<<"******"<<cntx<<" "<<cnty<<endl;
        g[cntx][cnty]=g[cnty][cntx]=min(t,g[cntx][cnty]);
    }
    for(int i=n+1;i<=n+m;i++)
        dfs(i);
   /// cout<<idx<<endl;
    if(idx==0) puts("No Solution");
    else{
        sort(a+1,a+1+idx,cmp);
        cout<<"G"<<a[1].id-n<<endl;
        printf("%.1lf %.1lf\n",1.0*a[1].minn,a[1].ave);
    }
    return 0;
}

posted @ 2021-04-04 10:30  OvO1  阅读(96)  评论(0编辑  收藏  举报