第十四届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组

文章提供了多个编程题目,包括日期统计、01串的熵、冶炼金属、飞机降落、接龙数列、岛屿个数、子串简写、整数删除、景区导游和砍树问题。每个题目都给出了详细的解题思路,涉及深度优先搜索(DFS)、二分查找、贪心策略、动态规划、线段树和并查集等算法。
摘要由CSDN通过智能技术生成

目录

试题 A: 日期统计 .本题总分:5 分

试题 B: 01 串的熵.本题总分:5 分

试题 C: 冶炼金属.本题总分:10 分

试题 D: 飞机降落.本题总分:10 分

试题 E: 接龙数列 .本题总分:15 分

试题 F: 岛屿个数 .本题总分:15 分

试题 G: 子串简写 .本题总分:20 分

试题 H: 整数删除.本题总分:20 分

试题 I: 景区导游 .本题总分:25 分

试题 J: 砍树 .本题总分:25 分


试题 A: 日期统计 .本题总分:5

 相同的日期你只需要统计一次即可,直接dfs剪枝跑就可以了 我的答案235

试题 B: 01 串的熵.本题总分:5

二分,l=1,r=23333333>>1 ,我的答案11027421

试题 C: 冶炼金属.本题总分:10 

对于最大值,可以根据整除分块定理2知道是a/b,然后取个min就是最大值,对于最小值,二分求,复杂度O(nlogn) 

然后其实知道r=a/b,那么也不难推出l是前一个块的r+1,也就是l=a/(b+1)+1 复杂度O(N)

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
#define x first
#define y second
#define pd push_back
const int maxn=1e4+10;
 
 
int n;
ll a[maxn],b[maxn];
 
void solve()
{
    cin>>n;
    ll ma=-1,mi=-1;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i]>>b[i];
        ll now=a[i]/b[i];
        if(ma==-1||ma>now)
        {
            ma=now;
        }
        mi=max(mi,(a[i]/(b[i]+1))+1);
    }
    cout<<mi<<" "<<ma;
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int tn=1;
    //cin>>tn;
    while(tn--)
    {
        solve();
    }
    return 0;
}

试题 D: 飞机降落.本题总分:10 

贪心寄了,还是全排列暴力~~~~~

还是dfs剪枝暴力吧,t<=10,n<=10

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
#define x first
#define y second
#define pd push_back
const int maxn=1e2+10;

struct node
{
    int t,d,l;
} ;
int n;
node a[maxn];
int f[maxn];
int flag;
void dfs(int len,int last)
{
    if(len==n)
    {
        flag=1;
        return;
    }
    for(int i=1;i<=n&&!flag;i++)
    {
        if(f[i])continue;
        auto [t,d,l]=a[i];
        if(last<=t+d)
        {
            f[i]=1;
            dfs(len+1,max(last+l,t+l));
            f[i]=0;
        }
    }
}
void solve()
{
    cin>>n;
    flag=0;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i].t>>a[i].d>>a[i].l;
        f[i]=0;
    }
    dfs(0,-1);
    if(flag)cout<<"YES"<<endl;
    else cout<<"NO"<<endl;
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int tn=1;
    cin>>tn;
    while(tn--)
    {
        solve();
    }
    return 0;
}

试题 E: 接龙数列 .本题总分:15 

考虑dp, dp[i],表示以i结尾的子序列最大长度,当前数字开头为j结尾为i,那么dp[i]=max(dp[i],dp[j]+1);,最后n-最长子序列就可以了,复杂度O(N)

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
#define x first
#define y second
#define pd push_back
const int maxn=1e5+10;
 
 
int n;
int dp[10];
/*
5
11 121 22 12 2023
1
*/
void solve()
{
    cin>>n;
    for(int i=1,j;i<=n;i++)
    {
        cin>>j;
        int f=j,d=j%10;
        while(f>=10)f/=10;
        dp[d]=max(dp[d],dp[f]+1);
    }
    int ma=1;
    for(int i=0;i<10;i++)
    {
        ma=max(ma,dp[i]);
        //cout<<dp[i]<<endl;
    }
    cout<<n-ma<<endl;
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int tn=1;
    //cin>>tn;
    while(tn--)
    {
        solve();
    }
    return 0;
}

试题 F: 岛屿个数 .本题总分:15 

 先从最外海水跑图,记录外部海水数量,岛的4周海水得有1个方向等于外部海水.否则就是环岛的子岛屿;

跑海水从8个方向染色;

然后跑周围是外部海水的岛屿,四个方向染++cnt,最后输出cnt就是答案

0(N*N)

#include <bits/stdc++.h>
#define int int64_t
#define endl '\n'
using namespace std;
const int MAX = 55;
const int INF = 0x3f3f3f3f3f3f3f3fll;
const int MOD = 1e9 + 7;

char map_[MAX][MAX];              // 地图
bool track[MAX][MAX] = {false};   // 访问标记
bool outsea[MAX][MAX] = {false};  // 外海标记
struct XY {
    int x, y;
} nxt[] = {
    {1, 0},
    {-1, 0},
    {0, 1},
    {0, -1},
    {1, 1},
    {-1, 1},
    {1, -1},
    {-1, -1},
};

void solve() {
    int n, m;
    cin >> n >> m;
    for (int i = 0; i < n; i++) {
        cin >> map_[i];
    }
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            track[i][j] = false;
            outsea[i][j] = false;
        }
    }
    // 预处理外海
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (map_[i][j] == '1' or track[i][j]) continue;
            bool yep = false;
            queue<XY> que, arr;
            track[i][j] = true;
            que.push({i, j});
            arr.push({i, j});  // 记录搜索到的所有海域
            while (not que.empty()) {
                XY pos = que.front();
                que.pop();
                // 注意:海域搜索可以往八个方向走,陆地搜索只能朝四个方向
                for (int i = 0; i < 8; i++) {
                    XY nw = {pos.x + nxt[i].x, pos.y + nxt[i].y};
                    if (0 <= nw.x and nw.x < n and 0 <= nw.y and nw.y < m) {
                        if (map_[nw.x][nw.y] == '0' and track[nw.x][nw.y] == false) {
                            track[nw.x][nw.y] = true;
                            que.push(nw);
                            arr.push(nw);
                        }
                    } else {
                        yep = true;  // 如果有一次搜索到地图外,标记此次搜索的所有区域为外海
                    }
                }
            }
            if (yep) {
                while (not arr.empty()) {
                    XY pos = arr.front();
                    arr.pop();
                    outsea[pos.x][pos.y] = true;  // 标记搜索到的海域为外海
                }
            }
        }
    }

    // 别忘了清空访问标记
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            track[i][j] = false;
        }
    }
    // 处理岛屿
    int cnt = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (map_[i][j] == '0' or track[i][j]) continue;
            bool yep = false;
            queue<XY> que;
            track[i][j] = true;
            que.push({i, j});
            while (not que.empty()) {
                XY pos = que.front();
                que.pop();
                for (int i = 0; i < 4; i++) {
                    XY nw = {pos.x + nxt[i].x, pos.y + nxt[i].y};
                    if (0 <= nw.x and nw.x < n and 0 <= nw.y and nw.y < m) {
                        if (map_[nw.x][nw.y] == '1') {
                            if (track[nw.x][nw.y] == false) {
                                track[nw.x][nw.y] = true;
                                que.push(nw);
                            }
                        } else {
                            if (outsea[nw.x][nw.y]) {
                                yep = true;  // 搜索到外海为外岛
                            }
                        }
                    } else {
                        yep = true;  // 搜索到边界外为外岛
                    }
                }
            }
            if (yep) {
                cnt++;  // 外岛计数
            }
        }
    }
    cout << cnt << endl;
}

int32_t main() {
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);

    int t;
    cin >> t;
    while (t--)
        solve();

    return 0;
}

试题 G: 子串简写 .本题总分:20 

把c1的位置存起来,对于每个c2位置二分找最小符合的位置,ans加上这个位置下标就可以了

也可以双指针暴力

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
#define x first
#define y second
#define pd push_back
#define pii pair<int,int>
const int maxn=5e5+10;
int n,k;
char s[maxn],a,b;
/*
4
abababdb a b
4
ba a b
*/
void solve()
{
    cin>>k;
    cin>>s+1;
    cin>>a>>b;
    n=strlen(s+1);
    vector<int>va,vb;
    for(int i=1;i<=n;i++)
    {
        if(s[i]==a)
        {
            va.pd(i);
        }
        if(s[i]==b)
        {
            vb.pd(i);
        }
    }
    va.pd(n+1);
    vb.pd(n+1);
    ll ans=0;
    for(int i=0;i<vb.size()-1;i++)
    {
        int r=lower_bound(va.begin(),va.end(),vb[i]-k+1)-va.begin();
        if(va[r]>vb[i]-k+1)
        {
            r--;
        }
        ans+=r+1;
        //cout<<r+1<<" "<<va[r]<<" "<<vb[i]<<" "<<ans<<endl;
    }
    cout<<ans<<endl;
 
}
 
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int tn=1;
    //cin>>tn;
    while(tn--)
    {
        solve();
    }
    return 0;
}

试题 H: 整数删除.本题总分:20 

 线段树+并查集

对于每个点并查集维护左父亲和右父亲。对于每次操作线段树找到最左侧最小下标,然后返回,

这个点的左父亲等于这个点左边的点的左父亲,右同理,然后线段树修改值即可

复杂度 O(KlogN)

可以也采用set存点值和下标,然后+并查集

复杂度也是O(KlogN)

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
#define x first
#define y second
#define pd push_back
#define pii pair<int,ll>
const int maxn=5e5+10;



int n,k;
ll a[maxn];
int l[maxn],r[maxn],f[maxn];
int findl(int x)
{
    return x==l[x]?x:l[x]=findl(l[x]);
}
int findr(int x)
{
    return x==r[x]?x:r[x]=findr(r[x]);
}

ll tr[maxn<<4];
void up(int rt)
{
    tr[rt]=min(tr[rt<<1],tr[rt<<1|1]);
}
void build(int rt,int l,int r)
{
    tr[rt]=a[l];
    if(l==r)return;
    int mid=l+r>>1;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
    up(rt);
}
pii cha(int rt,int l,int r)
{
    if(l==r)
    {
        return {l,tr[rt]};
    }
    int mid=l+r>>1;
    pii ans;
    if(tr[rt<<1]<=tr[rt<<1|1])
    {
        ans=cha(rt<<1,l,mid);
    }else
    {
        ans=cha(rt<<1|1,mid+1,r);
    }
    return ans;
}
void add(int rt,int l,int r,int id,ll val)
{
    if(id==l&&id==r)
    {
        tr[rt]+=val;
        a[l]=tr[rt];
        return;
    }
    int mid=l+r>>1;
    if(l<=id&&id<=mid)add(rt<<1,l,mid,id,val);
    else if(id>mid&&id<=r)add(rt<<1|1,mid+1,r,id,val);
    up(rt);
}
/*
5 3
1 4 2 8 7
5 4
1 4 2 8 7
5 5
1 4 2 8 7
*/
void solve()
{
    cin>>n>>k;
    l[0]=0;l[n+1]=0;
    r[0]=0;r[n+1]=0;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        l[i]=i;r[i]=i;
        f[i]=1;
    }
    build(1,1,n);
    while(k--)
    {
        ll mi=1e18,id=-1;
//        for(int i=1;i<=n;i++)//可用线段树优化
//        {
//            if(mi>a[i])
//            {
//                mi=a[i];
//                id=i;
//            }
//        }
        pii i=cha(1,1,n);
        id=i.x;mi=i.y;
        f[id]=0;
        l[id]=findl(l[id-1]);
        r[id]=findr(r[id+1]);
        if(l[id]>=1)add(1,1,n,l[id],mi);
        if(r[id]<=n)add(1,1,n,r[id],mi);
        add(1,1,n,id,1e17);
//        a[l[id]]+=a[id];
//        a[r[id]]+=a[id];
//        a[id]=1e18;
//        for(int i = 1; i <= n; i++)
//        {
//            if(f[i])
//            {
//                cout << a[i] << " ";
//            }
//        }
//        cout<<endl;
    }
    int ff=1;
     for(int i = 1; i <= n; i++)
        {
            if(f[i])
            {
                if(ff)ff=0;
                else cout<<" ";
                cout << a[i] ;
            }
        }
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int tn=1;
    //cin>>tn;
    while(tn--)
    {
        solve();
    }
    return 0;
}

试题 I: 景区导游 .本题总分:25 分

倍增,由于要按顺序去游览点,考虑起点那么只有A1,A2,对于2-k的点都是以A1为起点,那么其实路都差不多,假设在这之中p点不去,减去p-1到p的路径值减去p到p+1的路径值,加上p-1到p+1的路径值即可

复杂度O(NlogN+KlogN)

对于加减路径值也可以用树链剖分或lct 复杂度O(NlogN+KlogN)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2e5 + 10;
int h[N], e[N], w[N], ne[N], idx = 0;
int lg[N], p[N][20], deep[N], a[N];
ll dis[N], n, k, sum = 0;
void dfs(int lst, int now) {
    deep[now] = deep[lst] + 1;
    p[now][0] = lst;
    for (int i = 1; (1 << i) <= deep[now]; i++)
        p[now][i] = p[p[now][i - 1]][i - 1];

    for (int i = h[now]; i; i = ne[i]) {
        int to = e[i], cost = w[i];
        if (to != lst) {
            dis[to] = dis[now] + cost;
            dfs(now, to);
        }
    }
}
int lca(int u, int v) {
    if (deep[u] < deep[v]) swap(u, v);
    while (deep[u] > deep[v])
        u = p[u][lg[deep[u] - deep[v]]];

    if (u == v) return u;
    else {
        for (int i = lg[deep[u]]; i >= 0; i--) {
            if (p[u][i] != p[v][i])
                u = p[u][i], v = p[v][i];
        }
    }
    return p[u][0];
}
inline void add(int u, int v, int t) {
    e[++idx] = v, w[idx] = t, ne[idx] = h[u], h[u] = idx;
}
inline ll ask(int u, int v) {
    if (u == 0 || v == 0) return 0;
    else return dis[u] + dis[v] - 2 * dis[lca(u, v)];
}
inline ll query(int i) {
    return ask(a[i - 1], a[i + 1]) - ask(a[i - 1], a[i]) - ask(a[i], a[i + 1]);
}
int main() {
    cin >> n >> k;
    for (int i = 1; i < n; i++) {
        int u, v, t;
        cin >> u >> v >> t;
        add(u, v, t);
        add(v, u, t);
    }
    for (int i = 2; i <= n; i++)lg[i] = lg[i >> 1] + 1;
    dfs(0, 1);
    for (int i = 1; i <= k; i++)cin >> a[i];
    for (int i = 2; i <= k; i++)sum += ask(a[i - 1], a[i]);
    for (int i = 1; i <= k; i++)cout << sum + query(i) << ' ';
}

试题 J: 砍树 .本题总分:25 分

倍增+树上差分,对于一个数对,我们对数对路径上的值+1,最后看是否有一条边的左右点的点值相等

复杂度O(NlogN+N)

其实要对于数对路径值+1,也可以使用树链剖分来写,这样复杂度也是O(NlogN)

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
#define x first
#define y second
#define pd push_back
#define pii pair<int,ll>
const int maxn=1e5+10;
 
 
struct beizeng
{
    int dp[22][maxn],deep[maxn];
    int val[maxn];
    vector<int>v[maxn];
    void add(int a,int b)
    {
        v[a].pd(b);
        v[b].pd(a);
    }
    void dfs(int x,int fa)
    {
        //dft[op]=x;
        //df[x]=op++;
        deep[x]=deep[fa]+1;
        dp[0][x]=fa;
        for(int i=1;(1<<i)<=deep[x];i++)
        {
            dp[i][x]=dp[i-1][dp[i-1][x]];
        }
        for(int y:v[x])
        {
            if(y==fa)continue;
            dfs(y,x);
        }
    }
    void dfs2(int x,int fa)
    {
        for(int y:v[x])
        {
            if(y==fa)continue;
            dfs2(y,x);
            val[x]+=val[y];
        }
    }
    int lca(int a,int b)
    {
        if(deep[a]>deep[b])swap(a,b);
        for(int i=20;i>=0;i--)
        {
            if(deep[a]<=deep[b]-(1<<i))
                b=dp[i][b];
        }
        if(a==b)
            return a;
        for(int i=20;i>=0;i--)
        {
            if(dp[i][a]!=dp[i][b])
            {
                a=dp[i][a];
                b=dp[i][b];
            }
        }
        return dp[0][a];
    }
}tree;
int n,m;
int a[maxn],b[maxn];
void solve()
{
    cin>>n>>m;
    for(int i=1;i<n;i++)
    {
        cin>>a[i]>>b[i];
        tree.add(a[i],b[i]);
    }
    tree.dfs(1,0);
    for(int i=1;i<=m;i++)
    {
        int x,y;
        cin>>x>>y;
        int c=tree.lca(x,y);
        tree.val[x]++;tree.val[y]++;
        tree.val[c]--;tree.val[tree.dp[0][c]]--;
    }
    tree.dfs2(1,0);
    int ans=-1;
    for(int i=n-1;i>=1;i--)
    {
       if(tree.val[a[i]]==m&&tree.val[b[i]]==m)
       {
           ans=i;
           break;
       }
    }
    cout<<ans<<endl;
}
 
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int tn=1;
    //cin>>tn;
    while(tn--)
    {
        solve();
    }
    return 0;
}

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值