2022年中国高校计算机大赛-团队程序设计天梯赛(GPLT)上海理工大学校内选拔赛

2022年中国高校计算机大赛-团队程序设计天梯赛(GPLT)上海理工大学校内选拔赛

2024.8.2 12:00————16:00

过题数790/1500
补题数943.33/1500

  • A+B Problem
  • Komorebi的数学课
  • 次佛锅
  • Setsuna的K数列
  • Wiki下象棋
  • 黄金律法
  • 天气预报
  • 叠硬币
  • A+B Problem again
  • 史东薇尔城
  • 取模
  • 剪绳子
  • 数硬币
  • 截肢葛瑞克
  • 上海施工小学

A - A+B Problem

题解:
给出一个长度为n的数组,对于数组中的每一个数字a,再数组中的其他数字中找到一个b使得a+b最大。输出n个数字。
找出数组最大值并判断有几个,有多个的话输出所有的数字加上最大值的结果即可,只有一个的话,最大的数字要加上第二大的数字,别的加上最大的数字即可。
代码:

#include<bits/stdc++.h>

using namespace std;
#define int long long
int a[100005];
int an[100005];

signed main() {
    int n;
    cin >> n;
    int ma = 0;
    int ma2 = 0;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        if(a[i] > ma)ma = a[i];
    }
    int t = 0;
    int wz;
    for (int i = 1; i <= n; i++) {
        if(ma == a[i]){
            wz = i;
            t++;
            continue;
        }
        an[i] = a[i]+ma;
        if(an[i] > ma2)ma2 = an[i];
    }
    if(t == 1) {
        for (int i = 1; i <= n; i++) {
            if(i == wz)an[i] = ma2;
            cout << an[i] << ' ';
        }
    }
    else {
        for (int i = 1; i <= n; i++) {
            if(a[i] == ma)an[i] = a[i]+ma;
            cout << an[i] << ' ';
        }
    }
    return 0;
}

B - Komorebi的数学课

题解:
输出n的n次方mod(n+2)
代码:

#include<bits/stdc++.h>

using namespace std;
#define int long long
int n;

int qpow(int base,int power,int mod) {
    int res = 1;
    while(power) {
        if(power & 1)res= res*base%mod;
        base= base*base%mod;
        power >>=1;
    }
    return res;
}

signed main() {
    cin >> n;
    cout << qpow(n,n,n+2);
    return 0;
}

C - 次佛锅

题解:
第一行给出一串字符串,表示这个食材有多少个,相同食材可能多次出现。
用map存取查找即可。
代码:

#include<bits/stdc++.h>

using namespace std;
#define int long long
int n;
string s;
int t;
map<string,int>mp;

signed main() {
    getline(cin,s);
    string p = " ";
    int lt = 0;
    for (int i = 0; i < s.length(); i++) {
        while(('a' <= s[i] && s[i] <= 'z') || 'A' <= s[i] && s[i] <= 'Z') {
            p = p+s[i];
            i++;
        }
        while('0' <= s[i] && s[i] <= '9') {
            lt = lt*10 + s[i]-'0';
            i++;
        }
        if(s[i] == ' ') {
            if('0' <= s[i-1]  && s[i-1] <= '9') {
                mp[p] += lt;
                p = ' ';
                lt = 0;
            }
    }
    }
    mp[p] += lt;
    cin >> t;
    while(t--) {
        string q;
        cin >> q;
        q = ' '+q;
        cout << mp[q] << endl;
    }
    return 0;
}

D - Setsuna的K数列

最近好像总是遇到类似题,不知道是不是我的错觉,就是这种用位运算代替直接运算的,会方便很多,但敏感度不够,更多的是看感觉。
题解:
给定俩个整数n和k,创建一个集合A,包含k的所有整数次幂,去A中任意个元素相加,并放进一个新的集合B,将B中的元素从小到大排序输出第n项。
可以考虑到其实就是从第1个数字到第n个数字,每个位上的1分别代表加上了k的i次方,且就是一个递增的序列,所以直接快速幂输出即可。
代码:

#include<bits/stdc++.h>

using namespace std;
#define int long long
int n;
string s;
int t;
map<string,int>mp;

signed main() {
    getline(cin,s);
    string p = " ";
    int lt = 0;
    for (int i = 0; i < s.length(); i++) {
        while(('a' <= s[i] && s[i] <= 'z') || 'A' <= s[i] && s[i] <= 'Z') {
            p = p+s[i];
            i++;
        }
        while('0' <= s[i] && s[i] <= '9') {
            lt = lt*10 + s[i]-'0';
            i++;
        }
        if(s[i] == ' ') {
            if('0' <= s[i-1]  && s[i-1] <= '9') {
                mp[p] += lt;
                p = ' ';
                lt = 0;
            }
    }
    }
    mp[p] += lt;
    cin >> t;
    while(t--) {
        string q;
        cin >> q;
        q = ' '+q;
        cout << mp[q] << endl;
    }
    return 0;
}

E - Wiki下象棋

当时写了dfs,怎么都想不起来bfs怎么写,最后果然是没有写出来。dfs一直tle,优化也没用。
题解:
t组数据,每组给出棋盘的行列数和每个棋子的个数,坐标,起点和终点。
bfs跑俩遍就可以,对于马脚特判一下。
代码:

#include<bits/stdc++.h>

using namespace std;
#define int long long
const int N = 310;
int dx[] = {-2,-2,-1,1,2,2,1,-1};
int dy[] = {-1,1,2,2,1,-1,-2,-2};
int t;
bool st[N][N];
int vis[N][N];
int n,m,k,a,b,c,d;
int ans = 1e9;
int ans1 = 1e9;
typedef pair<int,int> PII;

void bfs(int x,int y) {
    queue<PII>q;
    memset(vis,-1,sizeof vis);
    vis[x][y] = 0;
    q.push({x,y});
    
    while(!q.empty()) {
    x=q.front().first;
    y=q.front().second;
    
    for (int i = 0; i < 8; i++) {
        int ax = x+dx[i],ay = y+dy[i];
        if(ax < 1 || ax > n || ay < 1 || ay > m)continue;
        if(st[ax][ay])continue;
        if(vis[ax][ay] == -1) {
            vis[ax][ay] = vis[x][y]+1;
            q.push({ax,ay});
        }
    }
        q.pop();
    }
    cout << vis[c][d] << ' ';
    return ;
}

void bfs1(int x,int y) {
    queue<PII>q;
    memset(vis,-1,sizeof vis);
    vis[x][y] = 0;
    q.push({x,y});
    while(!q.empty()) {
        x = q.front().first;
        y = q.front().second;
    for (int i = 0; i < 8; i++) {
        int ax = x+dx[i],ay = y+dy[i];
        if(ax < 1 || ax > n || ay < 1 || ay > m)continue;
        if(dx[i] == -2 && st[x-1][y])continue;
        if(dx[i] == 2 && st[x+1][y])continue;
        if(dy[i] == 2 && st[x][y+1])continue;
        if(dy[i] == -2 && st[x][y-1])continue;
        if(st[ax][ay])continue;
                if(vis[ax][ay] == -1) {
            vis[ax][ay] = vis[x][y]+1;
            q.push({ax,ay});
        }
    }
        q.pop();
    }
    cout << vis[c][d] << endl;
    return ;
}

signed main() {
    cin >> t;
    while(t--) {
        memset(st,0,sizeof st);
        cin >> n >> m >> k >> a >> b >> c >> d;
        while(k--) {
            int la,lb;
            cin >> la >> lb;
            st[la][lb] = true;
        }
        ans = 5000;
        memset(vis,0,sizeof vis);
        bfs(a,b);
        ans1 = 5000;
        memset(vis,0,sizeof vis);
        bfs1(a,b);
    }
}

F - 黄金律法

很奇怪的一道题,当时看过的人多直接试了一下,结果竟然过了。好像是不等式的知识?
题解:
有n个武器的属性值和n个魔法的属性值,现在要求单个属性对应单个魔法相乘求和的最小值。
一个递增一个递减相乘求和即可。
代码:

#include<bits/stdc++.h>

using namespace std;
#define int long long
const int N = 1e6+10;
int t;
int w[N],m[N];

signed main() {
    cin >> t;
    while(t--) {
        int n;
        cin >> n;
        for (int i = 1; i <= n; i++)cin >> w[i];
        sort(w+1,w+1+n);
        for (int i = 1; i <= n; i++)cin >> m[i];
        sort(m+1,m+1+n,greater<int>());
        int ans = 0;
        for (int i = 1; i <= n; i++) {
            ans += (w[i]*m[i]);
        }
        cout << ans << endl;
    }
    return 0;
}

G - 天气预报

题解:
未来n天的天气用一个字符串来表示,0表示晴朗,1表示会下青蛙。现在要求找出区间,可以一天都不选,使得他在这段时间内至少可以出去玩a天,休息b天。
对于这段区间的左节点循环遍历,右节点二分判断找到最接近的满足条件的日子,后面的所有日子都满足,直接相加就可以,很简单的二分。
代码:

#include<bits/stdc++.h>

using namespace std;
#define int long long
#define ll long long
const int N = 1e6+10;
int n,a,b;
int aa[N],bb[N];
//晴朗 休息

int check(int i,int j) {
    ll p = aa[j] - aa[i-1];
    ll q = bb[j] - bb[i-1];
    if(p >= a && q >= b)return 1;
    return 0;
}

signed main() {
    cin >> n >> a >> b;
    string s;
    cin >> s;
    s = ' '+s;
    for (int i = 1; i <= n; i++) {
        if(s[i] == '0') {
            aa[i] = aa[i-1]+1;
            bb[i] = bb[i-1];
        }
        else {
            bb[i] = bb[i-1]+1;
            aa[i] = aa[i-1];
        }
    }
     int ans = 0;
//     int m = 0;
//     for (int i = 1; i <= n; i++) {
//         if(aa[i]-aa[m] >= a && bb[i]-bb[m] >= b){
//             ans += (n-i+1);
//             m++;
//         }
//     }
//     for (int i = m; i <= n; i++) {
//         if(aa[n]-aa[i] >= a && bb[n]-bb[i] >= b) {
//             ans++;
//         }
//         else break;
//     }
    if(a==0&&b==0)ans++;//特殊情况:当a和b均为0时,可以一天也不选
    for(int i=1;i<=n;i++){
        ll l=i,r=n;
        while(l<r){
            ll mid=(l+r)/2;
            if(check(i,mid))r=mid;
            else l=mid+1;
        }
        if(check(i,l))ans+=(n-l+1);
    }
   cout << ans << endl;
    return 0;
}

J - 史东薇尔城

题解:
有n个节点和m条边,询问t次,每次si,ti。需要从si跑回1再跑到ti,球最短路径。
也就是从1跑到si再跑到ti。跑一遍Dijkstra然后输出最短距离即可。
代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6+10;
int n,m;
int dis[N],vis[N];
const int INF = 0x3f3f3f3f;
typedef pair<int,int>PII;
#define endl "\n"

struct ty {
    int to,next,w;
}edge[N+1000];

int cnt = 0;
int head[N];
void add_edge(int u,int v,int w) {
    cnt++;
    edge[cnt].to = v;
    edge[cnt].w = w;
    edge[cnt].next = head[u];
    head[u] = cnt;
}

void dij(int x) {
    priority_queue<PII,vector<PII>,greater<PII>>pq;
    pq.push({0,x});
    dis[x] = 0;
    while(!pq.empty()) {
        int x = pq.top().second;
        pq.pop();
        //一开始一直写在continue以后跑死循环了
        if(vis[x])continue;
        vis[x] = 1;
        for (int i = head[x]; i!=-1; i = edge[i].next) {
            if(!vis[edge[i].to]) {
                if(dis[edge[i].to] > dis[x] + edge[i].w) {
                    dis[edge[i].to] = dis[x] + edge[i].w;
                    pq.push({dis[edge[i].to],edge[i].to});
                }
            }
        }
    }
}

signed main( ){
    scanf("%lld %lld",&n,&m);
    memset(head,-1,sizeof head);
    int a,b,c;
    for (int i = 1; i <= m; i++) {
        scanf("%lld %lld %lld",&a,&b,&c);
        add_edge(a,b,c);
        add_edge(b,a,c);
    }
    int t;
    scanf("%lld",&t);
    memset(dis,INF,sizeof dis);
    memset(vis,0,sizeof vis);
    dij(1);
    //别再里面跑!浪费时间!
    int si,ti;
    while(t--) {
        cin >> si >> ti;
        printf("%lld\n",dis[si]+dis[ti]);
    }
    return 0;
}

L - 剪绳子

题解:
十米的绳子,C表示在这个位置减一刀,A表示在这个位置询问这条绳子的长度。
怀疑是数据太水了,暴力跑了一遍就过了。
官方给出的题解是一开始把所有点当成一个绳子,然后把每一截绳子练成一个并查集,倒着往前遍历,C表示连上这俩个并查集,A表示查询。储存输出。
代码:

#include<bits/stdc++.h>

using namespace std;
#define int long long
set<double>p;

signed main() {
    int q;
    cin >> q;
    while(q--) {
        char s;
        cin >> s;
        double m;
        cin >> m;
        if( s== 'A') {
            double gg = 0;
            double now = 10;
            for (auto x: p) {
                if(x > m){
                    now = x;
                    break;
                }
                else gg = x;
            }
            double res = now-gg;
            printf("%0.5lf\n",res);
        }
        else {
            p.insert(m);
            
        }
    }
    return 0;
}

  • 12
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值