首页 > 其他分享 >3 月 1 日测试题解

3 月 1 日测试题解

时间:2023-03-05 17:33:55浏览次数:38  
标签:std le 测试题 int text s1 ans

3 月 1 日测试题解

T1

题意

给你一个 \(n \times m\) 的 01 矩形,求一个面积最大的不包含数字 1 的矩形。

\(n,m \le 1000\)。

思路

首先,这道题的数据水到了什么程度呢?\(O(n ^ 3)\) 的暴力可以轻松跑过。所以我个人认为这道题没有任何价值。

\(\text{20pts(Maybe)}\)

我们考虑直接枚举矩形的上下与左右边界,显然这样做的复杂度为 \(O(n ^ 4)\)。

\(\text{60pts(Maybe)}\)

上边的做法有一个显而易见的优化:可以预处理出一个数组 \(f_{i, j}\),表示从第 \(i\) 行第 \(j\) 列最多能向上走的格数,这样,我们可以不用枚举上边界,时间复杂度降为 \(O(n ^ 3)\)。

\(\text{100pts}\)

分析时间复杂度高在什么地方,发现枚举下边界的过程是无法优化的,于是只能从枚举左右边界的部分下手。

我们发现,枚举左右边界的部分实质上是在求从第 \(i\) 行第 \(j\) 列能够向左右拓展的最大宽度,也就是要在左右各找到第一个其 \(f\) 值小于 \(f_{i, j}\) 的一列。而求第一个比某个值小的值,我们不难想到使用单调栈来维护。这样,枚举左右边界的时间复杂度降为线性,总体时间复杂度为 \(O(n ^ 2)\)。

代码

\(\text{100pts}\)

我使用的方法跑了两遍单调栈,但事实上有只用一遍的更优秀做法。

#include <bits/stdc++.h>

using i64 = long long;

int main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(0);
  
  int n, m;
  std::cin >> n >> m;
  std::vector<std::vector<int>> v(n + 1, std::vector<int>(m + 1, 0));
  std::vector<std::vector<int>> f(n + 1, std::vector<int>(m + 1, 0));
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      std::cin >> v[i][j];
    }
  }
  
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      if (v[i][j] == 0) {
        f[i][j] = f[i - 1][j] + 1;
      }
    }
  }
  
  int ans = 0;
  std::vector<int> tmp(m + 1, 0);
  std::stack<int> s1;
  for (int i = 1; i <= n; i++) {
    tmp.clear();
    tmp.resize(m + 1, 0);
    while (!s1.empty()) {
      s1.pop();
    }
    
    for (int j = 1; j <= m; j++) {
      int cur = f[i][j];
      while (!s1.empty() && f[i][s1.top()] >= cur) {
        s1.pop();
      }
      if (s1.empty()) {
        tmp[j] = j;
      } else {
        tmp[j] = j - s1.top();
      }
      s1.push(j);
    }
    
    while (!s1.empty()) {
      s1.pop();
    }
    
    for (int j = m; j >= 1; j--) {
      int cur = f[i][j];
      while (!s1.empty() && f[i][s1.top()] >= cur) {
        s1.pop();
      }
      if (s1.empty()) {
        tmp[j] += m - j;
      } else {
        tmp[j] += s1.top() - j - 1;
      }
      s1.push(j);
    }
    
    for (int j = 1; j <= m; j++) {
      // std::cout << tmp[j] << " \n"[j == m];
      ans = std::max(ans, tmp[j] * f[i][j]);
    }
  }
  
  std::cout << ans << "\n";
  
  return 0;
}

T2

题意

给你一棵 \(n\) 个节点的树,问每个节点到其余节点的最大距离。

\(n \le 10000\)。

思路

\(\text{60pts(Maybe)}\)

考虑枚举每个节点,并以其为根做一遍 DFS 求出其到其余节点的距离,然后在其中找出最大的。时间复杂度 \(O(n ^ 2)\)。

\(\text{100pts}\)

我们首先求出这棵树上的任意一条直径,对于直径上的点,其到其他端点的最大距离显然是其到直径左端点距离与到右端点距离的较大值,否则这条链无法成为直径。而对于不在直径上的节点,我们可以认为其是“挂”在直径上的某个点上的,于是对于这个点,要求的就是它与它的“挂载点”的距离与其“挂载点”的答案之和。这个也可以用反证法证明。时间复杂度为 \(O(n)\)。

但是这道题还有一个玄之又玄的树剖写法?反正我不会。

代码

\(\text{100pts}\)

#include <bits/stdc++.h>

using i64 = long long;

static const int INF = 0x3f3f3f3f;

template<int N>
struct Tree {
  int n;
  std::vector<int> e[N], f[2], dis, ans, nxt;
  std::vector<bool> vis;
  int L, R, D;
  
  Tree(int num) {
    f[0].resize(N, 0);
    f[1].resize(N, 0);
    dis.resize(N, 0);
    ans.resize(N, 0);
    nxt.resize(N, 0);
    vis.resize(N, 0);
    n = num;
    L = 0, R = 0, D = 0;
    return;
  }
  
  void add(int u, int v) {
    e[u].push_back(v);
    return;
  }
  
  void dfs(int u, int from) {
    dis[u] = dis[from] + 1;
    nxt[u] = from;
    for (auto v : e[u]) {
      if (v == from) {
        continue;
      }
      dfs(v, u);
    }
    return;
  }
  
  void get() {
    dfs(1, 0);
    for (int i = 1; i <= n; i++) {
      if (dis[L] < dis[i]) {
        L = i;
      }
    }
    dfs(L, 0);
    for (int i = 1; i <= n; i++) {
      if (dis[R] < dis[i]) {
        R = i;
      }
    }
    D = dis[R] - dis[L];
    return;
  }
  
  void dfs2(int u, int from, int pivot) {
    for (auto v : e[u]) {
      if (v == from || vis[v]) {
        continue;
      }
      dis[v] = dis[u] + 1;
      ans[v] = dis[v] + ans[pivot];
      dfs2(v, u, pivot);
    }
    return;
  }
  
  void solve() {
    int pivot = 0;
    for (int i = R; i; i = nxt[i]) {
      ans[i] = std::max(D - pivot, pivot);
      dis[i] = 0;
      vis[i] = true;
      pivot++;
    }
    for (int i = R; i; i = nxt[i]) {
      dfs2(i, 0, i);
    }
    for (int i = 1; i <= n; i++) {
      std::cout << ans[i] << "\n";
    }
    return;
  }
};

int main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(0);
  
  int n;
  std::cin >> n;
  Tree<10050> G(n);
  for (int i = 1; i < n; i++) {
    int u, v;
    std::cin >> u >> v;
    G.add(u, v);
    G.add(v, u);
  }
  
  G.get();
  G.solve();
  
  return 0;
}

T3

题意

给你一个 \(n\) 个点 \(m\) 条边的带权无向图与 \(k\) 条可能的从 \(1\) 到 \(n\) 的路径。你要判断哪条路径最短并输出这条路径的总长度。若无合法路径,输出 \(\texttt{Wrong}\)。

\(n \le 1000\),\(m \le 500000\),\(k \le 100\),不保证没有重边,保证合法的路径经过的节点数不超过 \(1000\)。

思路

\(\text{100pts}\)

这道题只有一个做法:模拟。而且丝毫没有思维难度。这道题恶心的地方在于其中的众多陷阱。

  • 题目没有保证输入一定合法,如 \(\texttt{I can't solve this!}\) 这种奇怪的字符串也是有可能出现的。
  • 题目中只规定了所经过的节点数,但并未保证其互不相同,也有可能一直在兜圈子。
  • 有的输入会在最后打一个空格,需要特殊处理。

这道题输入的毒瘤程度超乎了我的想象。调了将近半个小时才发现最后有一个空格。

代码

\(\text{100pts}\)

#include <bits/stdc++.h>

using i64 = long long;
using vdb = std::vector<long double>;

static const long double INF = 1e18;

void read(int &x) {
  x = 0;
  char c = getchar();
  while (!isdigit(c)) c = getchar();
  while (isdigit(c)) x = 10 * x + c - '0', c= getchar();
}

int main() {
  // std::ios::sync_with_stdio(false);
  // std::cin.tie(0);
  
  int n, m, k;
  std::cin >> n >> m >> k;
  std::vector<vdb> e(n + 1, vdb(n + 1, INF));
  for (int i = 1; i <= n; i++) {
    e[i][i] = 0;
  }
  
  for (int i = 1; i <= m; i++) {
    int u, v;
    long double w;
    read(u);
    read(v);
    scanf("%llf", &w);
    e[u][v] = std::min(e[u][v], w);
    e[v][u] = e[u][v];
  }
  
  std::cin.get();
  
  long double ans = INF;
  for (int i = 1; i <= k; i++) {
    std::vector<int> path;
    std::string s;
    
    std::getline(std::cin, s);
    
    if (s[s.length() - 1] == ' ') {
      s = s.substr(0, s.length() - 1);
    }
    
    int cnt = 0, len = s.length();
    bool isBad = false;
    for (int i = 0; i <= len; i++) {
      if (i != len && !isdigit(s[i]) && s[i] != ' ') {
        isBad = true;
        break;
      }
      if (i == len || s[i] == ' ') {
        if (path.empty() || cnt != *(path.end() - 1)) {
          path.push_back(cnt);
        }
        cnt = 0;
      } else {
        cnt = cnt * 10 + s[i] - '0';
      }
    }
    
    if (isBad) {
      continue;
    } 
    
    if (path[0] != 1 || *(path.end() - 1) != n) {
      continue;
    }
    
    int lst = path[0], cur = 0;
    long double curAns = 0;
    bool flg = true;
    for (int i = 1; i < path.size(); i++) {
      cur = path[i];
      if (cur > n || cur < 1 || e[lst][cur] == INF) {
        flg = false;
        break;
      }
      curAns += e[lst][cur];
      lst = cur;
    }
    
    if (!flg) {
      continue;
    }
    
    ans = std::min(curAns, ans);
  }
  
  if (ans == INF) {
    std::cout << "Wrong" << "\n";
  } else {
    std::cout << std::fixed << std::setprecision(4) << ans << "\n";
  }
  return 0;
}

T4

题意

求长度为 \(n\) 的不包含超过 \(l\) 个 1 的第 \(i\) 个 01 串。

对于 \(40\%\) 的数据,\(1 \le n \le 100\)。
对于 \(100\%\) 的数据,\(1 \le l \le n \le 31\),保证 \(i\) 有意义。

思路

又是一道没有意义的题。

\(\text{40pts}\)

我们考虑枚举每一个属于 \([1, n]\) 的正整数并判断其合法性。时间复杂度 \(O(n \log n)\)。

\(\text{100pts(Fake)}\)

我们知道 gcc 内置了一个函数叫做 __builtin_popcount(),可以在 \(O(1)\) 时间内计算出一个数的二进制表达中的 1 的个数。

仍然使用上边的做法,这样时间复杂度降为 \(O(n)\)。

什么?你不信这能过?LOJ 的机子跑 \(10^9\) 的数据只需要不到 400ms!

\(\text{100pts}\)

显然,答案是具有单调性的,但是不是严格单调。也就是说可以二分答案,然后用数位 DP 验证这个答案即可。

这个数位 DP 比板子还板子,应该不会错吧。

但这道题有个很恶心的地方,就是你这样二分出来的答案不一定是合法的,还要判断一下合法性,不合法的话要一直自增。

代码

\(\text{100pts}\)

#include <bits/stdc++.h>

using i64 = long long;

static const int N = 60;
i64 f[N][2][N];
i64 n, L, I;
int nums[N], len;

i64 dfs(int pos, int cur, int all, bool isLimited) {
  if (all > L) {
    return 0;
  }
  if (!pos) {
    return all <= L;
  }
  if (!isLimited && ~f[pos][cur][all]) {
    return f[pos][cur][all];
  }
  int up = isLimited ? nums[pos] : 1;
  i64 res = 0;
  for (int i = 0; i <= up; i++) {
    res += dfs(pos - 1, i, all + (i == 1), isLimited && i == up);
  }
  if (!isLimited) {
    f[pos][cur][all] = res;
  }
  return res;
}

i64 calc(i64 mid) {
  if (mid == 0) {
    return 1;
  }
  len = 0;
  i64 res = 0;
  while (mid) {
    nums[++len] = mid & 1;
    mid >>= 1;
  }
  for (int i = 0; i <= nums[len]; i++) {
    res += dfs(len - 1, i, i == 1, i == nums[len]);
  }
  return res;
}

int main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(0);
  
  memset(f, -1, sizeof(f));
  
  std::cin >> n >> L >> I;
  
  i64 l = 0, r = 1ll << n;
  
  while (calc(l) + 1 < calc(r)) {
    i64 mid = (l + r) >> 1;
    if (calc(mid) > I) {
      r = mid;
    } else {
      l = mid;
    }
  }

  while (__builtin_popcount(l) > L && calc(l) < I - 1) {
    l++;
  }

  std::vector<int> ans;
  while (l) {
    ans.push_back(l & 1);
    l >>= 1;
  }
  while (ans.size() < n) {
    ans.push_back(0);
  }
  std::reverse(ans.begin(), ans.end());
  for (auto i : ans) {
    std::cout << i;
  }
  std::cout << "\n";
  
  return 0;
}

标签:std,le,测试题,int,text,s1,ans
From: https://www.cnblogs.com/forgot-dream/p/17181009.html

相关文章

  • 今日课上测试题总结-计算最长英语单词链
    今天软工课上老师留的作业总结一下1importjava.io.*;2importjava.util.*;34publicclassMaxlist{56publicstaticvoidmain(String[]args)th......
  • L5-NOIP模拟11 测试题解
    经典老题排名-L5-NOIP模拟11-码创未来A.[CQOI2009]中位数洛谷P1627|码创未来题意给出\(1,2,\dots,n\)的一个排列,统计该排列有多少个长度为奇数的连续子串......
  • 部分互测题,专项测试题题解
    互测部分1https://www.cnblogs.com/Chencgy/p/16970117.html2A.营救皮卡丘跑弗洛伊德,搞出\(i->j\)不经过比\(i,j\)编号大的点的最小花费每个点都要走一遍,套......
  • 万喜人《平面几何测试题集》题记
    更新到测试9暂时做的不是很好,日后完善常规题目通过倒角,全等,相似常规辅助线模块构型中的初级知识可以完成的题目[1]1,2,5(同一法)[2]1[3]1,2(等角共轭点),3......
  • JAVA结业测试题及答案
    JAVA结业试题二1下列哪些方法互为重载方法,哪些方法是覆盖方法。答案用号码标识:4分publicclassA{  ①publicstaticvoidmethod(Stringstr){};  ②protected int......
  • 软件测试题库小程序推荐 这个免费题库,你用绝对停不下来
    对于已经掌握了如测试基础技能的人来说,最需要的就是进入到软件测试岗位上去工作,但由于面试是从业者必须经历的一个环节,而很多测试人因为也是才好踏入测试边缘,并没有相关的岗......
  • 数据结构与算法测试题
    1.完全二叉树的第5层有9个节点,该完全二叉树总计有多少个节点( B   ).A.41B.24C.40D.25完全二叉树,说明前四层都是满结点,第五层有九个结点,故有:2^4 -1=15     ......
  • 操作系统 测试题
    一、单选1、下面哪项不是常用调度算法A、FCFS B、SJF C、HRN D、ABC2、响应比的计算方法是A、(作业等待时间+作业执行时间)/作业执行时间B、(作业等待时间+作业执行时间)/......
  • 【MySQL】测试题01
    ✅作者简介:热爱国学的Java后端开发者,修心和技术同步精进。......
  • 2022期中测试题目-校园社团活动管理系统
    题目要求:校园社团作为高校课外活动的重要组成部分,发展十分迅速,也受到越来越多学生的欢迎,社团规模、数量等都在日益增长,社团活动也更为多样和丰富。然而,大多数高校还没有一......