NewOJ Week 9题解

比赛链接:http://oj.ecustacm.cn/contest.php?cid=1024

题目总览

题目TAG难度来源补题链接
平均数组构造 C o d e C h e f CodeChef CodeChefhttp://oj.ecustacm.cn/problem.php?id=1831
配对贪心☆☆ C o d e C h e f CodeChef CodeChefhttp://oj.ecustacm.cn/problem.php?id=1832
可逆循环字符串思维题☆☆☆ P A C N W   2021 PACNW \ 2021 PACNW 2021http://oj.ecustacm.cn/problem.php?id=1833
树与排列暴力、 d f s dfs dfs l c a lca lca☆☆☆ P A C N W   2021 PACNW \ 2021 PACNW 2021http://oj.ecustacm.cn/problem.php?id=1834
跳房子游戏动态规划☆☆☆☆ P A C N W   2021 PACNW \ 2021 PACNW 2021http://oj.ecustacm.cn/problem.php?id=1835

A 平均数组

题意:

在这里插入图片描述

Tag: 构造

难度:

来源: C o d e C h e f CodeChef CodeChef

思路: 由于任意一组解即可,可以考虑以 X X X为中位数连续数组。

如果 N N N为偶数,则可以构造 [ X − N 2 , X − 1 ] ∪ [ X + 1 , X + N 2 ] [X-\frac{N}{2},X-1] \cup[X+1,X+\frac{N}{2}] [X2N,X1][X+1,X+2N]

如果 N N N为奇数,则可以构造 [ X − N 2 , X + N 2 ] [X-\frac{N}{2},X+\frac{N}{2}] [X2N,X+2N]

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        int n, x;
        cin >> n >> x;
        int length = n / 2;
        for(int i = x - length; i <= x - 1; i++)
            cout<<i<<" ";
        if(n & 1)cout<<x<<" ";
        for(int i = x + 1; i <= x + length; i++)
            cout<<i<<" ";
        cout<<endl;
    }
    return 0;
}

B 配对

题意:

在这里插入图片描述

Tag: 贪心

难度: ☆☆

来源: C o d e C h e f CodeChef CodeChef

思路: A [ i ] A[i] A[i] B [ j ] B[j] B[j]进行配对累计 N N N组,最小化 m a x A [ i ] + B [ j ] max{A[i]+B[j]} maxA[i]+B[j]。使用贪心的思想,想让最大的和最小化,相当于较大的数字和较小的数字配对,这样可以使得最大值尽可能小。

则想法为: A A A从小到大排序, B B B从小到大排序, A A A最小的和 B B B最大的配对。这样可以保证每个大的数字匹配到最小的数字,从而使得最大和最小化。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e4 + 10;
int a[maxn], b[maxn];
int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        int n;
        cin >> n;
        for(int i = 1; i <= n; i++)cin >> a[i];
        for(int i = 1; i <= n; i++)cin >> b[i];
        sort(a + 1, a + 1 + n);
        sort(b + 1, b + 1 + n);
        int ans = 0;
        for(int i = 1; i <= n; i++)
            ans = max(ans, a[i] + b[n + 1 - i]);
        cout<<ans<<endl;
    }
    return 0;
}

C 可逆循环字符串

题意:

在这里插入图片描述
Tag: 思维题

难度: ☆☆☆

来源: P A C N W   2021 PACNW \ 2021 PACNW 2021

思路: 根据题意描述,要使得 s s s的所有子串的翻转结果均为 s s s的循环子串,等价于s翻转结果为 s s s的循环子串。

因为 s s s也是 s s s的子串,如果 s s s已经满足,则 s s s的子串一定满足,反之亦然。这是一个充要条件。

所以题目变成:给定字符串 s s s,判断 s s s的翻转结果是否为 s s s的循环子串。

循环子串的判定:直接把 s s s变成 s + s s+s s+s然后判断是否为 s + s s+s s+s的子串。

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        string s;
        cin >> s;
        string ss = s + s;
        reverse(ss.begin(), ss.end());
        cout << (ss.find(s) != string::npos) << endl;
    }
    return 0;
}

D 树与排列

题意:

在这里插入图片描述

Tag: 暴力、 d f s dfs dfs l c a lca lca

难度: ☆☆☆

来源: P A C N W   2021 PACNW \ 2021 PACNW 2021

思路: 要求树上两个节点的距离,可以直接使用 L C A LCA LCA模板即可。

但是由于题目只需要判断距离是否小于等于 3 3 3,那么直接暴力即可。

类似 L C A LCA LCA,预处理每个点的深度 d e p t h depth depth和父节点 p r e pre pre

对于相邻两个点 x x x y y y

1、如果深度不同,则深度大的往上走,最多走 3 3 3步即可判断是否合法。

2、如果深度相同,则判断是否 x x x已经等于 y y y,如果不等, x x x y y y同时往上走 1 1 1步。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int n;
vector<int>G[maxn];
int a[maxn];
int depth[maxn];//depth[i]表示节点i的深度
int pre[maxn];  //pre[i]表示节点i的父节点

void dfs(int u, int fa, int d)
{
    pre[u] = fa;
    depth[u] = d;
    for(auto v : G[u])if(v != fa)
        dfs(v, u, d + 1);
}

int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        for(int i = 1; i <= n; i++)
            G[i].clear();
        for(int i = 1; i < n; i++)
        {
            int u, v;
            scanf("%d%d", &u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        for(int i = 1; i <= n; i++)
            scanf("%d", &a[i]);
        dfs(1, -1, 0);
        int ok = 1;
        for(int i = 1; i + 1 <= n && ok; i++)
        {
            int x = a[i], y = a[i + 1], dis = 0;
            while(dis <= 3 && depth[x] > depth[y])x = pre[x], dis++;
            while(dis <= 3 && depth[y] > depth[x])y = pre[y], dis++;
            while(dis <= 3 && depth[x] && x != y)x = pre[x], y = pre[y], dis += 2;
            if(dis > 3)ok = 0;
        }
        cout<<ok<<endl;
    }
    return 0;
}

E 跳房子游戏

题意:

在这里插入图片描述
Tag: 动态规划

难度: ☆☆☆☆

来源: P A C N W   2021 PACNW \ 2021 PACNW 2021

思路: 每个格子的数字只能是 1 − K 1-K 1K,只能从 1 1 1开始到 K K K结束。

每个格子有三个属性: x , y , n u m x,y,num x,y,num,表示第 x x x行第 y y y列数字为 n u m num num

因为每次从 1 1 1开始到 K K K结束,所以按照数字大小排序。

数值 n n n按照从 1 1 1 K K K枚举,记 c c [ x ] cc[x] cc[x]表示走到数值为 n − 1 n-1 n1且到达第 x x x行的最短路径长度, r c [ y ] rc[y] rc[y]表示到达第 y y y列的最短路径长度,可以根据 r c , c c rc,cc rc,cc求出到达数值 n n n的最短路径长度 d d d

对于每一个数值为 n n n的格子 ( x , y ) (x,y) (x,y)而言,可以更新 d d d,同时可以更新下一轮的 c c , r c cc,rc cc,rc数组,直到走到数值为 K K K停止。

时间复杂度 O ( N 3 ) O(N^3) O(N3)

#include <algorithm>
#include <iostream>
#include <tuple>
#include <vector>
using namespace std;

int main() {
  int N, K;
  while (cin >> N >> K) {
    vector<vector<int>> G(N, vector<int>(N));
    vector<tuple<int, int, int>> v;
    for (int y = 0; y < N; y++)
    for (int x = 0; x < N; x++) {
      cin >> G[y][x];
      v.emplace_back(G[y][x], x, y);
    }
    sort(v.begin(), v.end());//按照数字大小排序

    vector<int64_t> rc(N), cc(N);
    int64_t ret = 1e18;
    for (int n = 1, i = 0; n <= K; n++) {//对于数值为n的格子
      auto orc = rc, occ = cc;
      rc = cc = vector<int64_t>(N, 1e18);
      for (; i < v.size(); i++) {//求出到达数值n的最小距离d
        auto [s, x, y] = v[i];
        if (s != n) break;       //只遍历数值为n的格子
        int64_t d = min(orc[y], occ[x]);
        if (s == K) ret = min(ret, d);
        for (int x2 = 0; x2 < N; x2++) cc[x2] = min(cc[x2], d + (x2-x)*(x2-x));
        for (int y2 = 0; y2 < N; y2++) rc[y2] = min(rc[y2], d + (y2-y)*(y2-y));
      }
    }
    cout << (ret == 1e18 ? -1 : ret) << endl;
  }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

傅志凌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值