Loading

2022.10.20 总结

1. 逐月 P5049

逐月 P5049

题意

有一个 \(n \times n\) 的矩阵,现在还有 \(k\)\(n \times n\) 的碎片,需要把两个碎片拼成这个矩阵,并且两个碎片只能水平或者垂直移动。

两块碎片中的 \(#\) 号不能重叠,也不能超出矩阵范围。

请你求出这个矩阵是由哪两个碎片组成的,答案唯一。

思路

100 分

因为只能水平和垂直移动,所以可以直接枚举两个碎片水平和垂直移动多少,然后暴力拼接两个碎片,检查是否和矩阵相同即可。

时间复杂度

枚举两个碎片,\(O(k ^ 2)\)

枚举水平和垂直的偏移量,\(O(n ^ 4)\)

拼接碎片和检查矩阵,\(O(n ^ 2)\)

总时间复杂度为 \(O(k ^ 2 \times n ^ 6)\)

空间复杂度

记录原本矩阵,\(O(n ^ 2)\)

记录碎片,\(O(k \times n ^ 2)\)

临时拼接碎片,\(O(n ^ 2)\)

总时间复杂度为 \(O(k \times n ^ 2)\)

#include <bits/stdc++.h>

using namespace std;

const int N = 10, K = 15;

int n, k, cnt;
bool h[N][N], p[K][N][N], f[N][N];

bool F(int id, int d1, int d2){  // 拼接
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= n; j++) {
      int nx = i + d1, ny = j + d2;
      if (min(nx, ny) >= 1 && max(nx, ny) <= n && p[id][i][j] && f[nx][ny]) {
        return 0;
      }
      if (nx < 1 || ny < 1 || nx > n || ny > n || f[nx][ny]) {
        continue;
      }
      f[nx][ny] = p[id][i][j];
    }
  }
  return 1;
}

bool P(){  // 检查
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= n; j++) {
      if (h[i][j] != f[i][j]) {
        return 0;
      }
    }
  }
  return 1;
}

void cl(){  // 清空
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= n; j++) {
      f[i][j] = 0;
    }
  }
}

int main(){
  freopen("bcs.in", "r", stdin);
  freopen("bcs.out", "w", stdout);
  cin >> n >> k;
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= n; j++) {
      char v;
      cin >> v;
      h[i][j] = (v == '#');
    }
  }
  for (int i = 1; i <= k; i++) {
    for (int j = 1; j <= n; j++) {
      for (int k = 1; k <= n; k++) {
        char v;
        cin >> v;
        p[i][j][k] = (v == '#');
      }
    }
  }
  for (int i = 1; i <= k; i++) {
    for (int j = i + 1; j <= k; j++) {
      for (int a = 1 - n; a <= n - 1; a++) {
        for (int b = 1 - n; b <= n - 1; b++) {
          for (int c = 1 - n; c <= n - 1; c++) {
            for (int d = 1 - n; d <= n - 1; d++) {
              if (F(i, a, b) && F(j, c, d) && P()) {
                cout << i << ' ' << j;
                return 0;
              }
              cl();
            }
          }
        }
      }
    }
  }
  return 0;
}

2. 逐月 P5063

逐月 P5063

题意

有一个 \(n \times n\) 的矩阵,Bessie 要从 \((1, 1)\) 走到 \((n, n)\)

请问有多少种不同的回文路径能让 Bessie 走到终点。

思路

36 分到 44 分

暴搜路径,判断是否为回文串以及是否之前出现过。

时间复杂度

回文路径长度最多为 \(2n - 1\),每个字符都可以往下走和往右走,\(O(2 ^ {2n})\)

检查是否出现过,最多 \(O(2 ^ {2n})\)

总时间复杂度为 \(O(2 ^ {2n})\)

空间复杂度

每次记录长度为 \(2n\) 的字符串,最多有 \(2 ^ n\) 种字符串,总空间复杂度为 \(O(n ^ {2n})\)

84 分

因为是回文路径,所以只要知道前半段就能求出后半段。

而每条路径的长度肯定是 \(2n - 1\),所以只要搜到 \(n\) 个字符就往后找后半段。

时间复杂度

枚举前半段,\(O(2 ^ n)\)

根据前半段找出后半段,\(O(2 ^ n)\)

检查是否出现过,\(O(2 ^ {2n})\)

总时间复杂度为 \(O(2 ^ {2n})\)

因为找路径的时间复杂度会变少,所以会比暴搜更快。

空间复杂度

每次记录长度为 \(2n\) 的字符串,最多有 \(2 ^ n\) 种字符串,总空间复杂度为 \(O(n ^ {2n})\)

100 分

因为检查是否出现过用的时间过多,所以可以用 \(map\) 或者 \(set\) 优化。

时间复杂度

枚举前半段,\(O(2 ^ n)\)

根据前半段找出后半段,\(O(2 ^ n)\)

检查是否出现过,\(O(\log cnt)\) (\(cnt\) 是目前有的回文路径的数量)。

总时间复杂度为 \(O(2 ^ {n} \times \log cnt)\)

空间复杂度

每次记录长度为 \(2n\) 的字符串,最多有 \(2 ^ n\) 种字符串,总空间复杂度为 \(O(n ^ {2n})\)

代码

#include <bits/stdc++.h>

using namespace std;

const int N = 20;

int n, ct;
char c[N][N];
string s;

set<string> t;

bool F(string s){  // 是否存在过
  if (t.find(s) != t.end()) {  // 如果没有,会返回 .end()
    return 0;
  }
  return 1;
}

bool P(int x, int y, int num){  // 是否可以搜到后半段
  if (x > n || y > n || c[x][y] != s[num]) {
    return 0;
  }
  return num == 1 || P(x + 1, y, num - 1) || P(x, y + 1, num - 1);
}

void dfs(int x, int y, int cnt){
  if (x < 1 || x > n || y < 1 || y > n) {
    return ;
  }
  s[cnt] = c[x][y];
  if (cnt == n) {
    if (P(x, y, n) && F(s)) {
      t.insert(s);
      ct++;
    }
    return ;
  }
  dfs(x, y + 1, cnt + 1);
  dfs(x + 1, y, cnt + 1);
}

int main(){
  freopen("palpath.in", "r", stdin);
  freopen("palpath.out", "w", stdout);
  cin >> n;
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= n; j++) {
      cin >> c[i][j];
    }
  }
  s.resize(n + 1);
  dfs(1, 1, 1);
  cout << ct;
  return 0;
}

也可以记录所有回文字符串,最后排序去重。

posted @ 2023-03-02 22:43  chengning0909  阅读(10)  评论(0编辑  收藏  举报