HW2-分治策略

A-Guess The Number

知识点

  • m=(l+r)/2可能会发生溢出。因此采用m=l+(r-l)/2m=l+((r-l)>>1)
  • 输出结束后刷新缓冲区

代码实现

C++

#include <iostream>
#define ull unsigned long long
using namespace std;
int main() {
  ull l = 0, r = 0xffffffffffffffff;
  while (l <= r) {
    ull m = l + (r - l) / 2;
    cout << m << endl;
    string res;
    cin >> res;
    if (res == "TooBig") {
      r = m - 1;
    } else if (res == "TooSmall") {
      l = m + 1;
    } else {
      return 0;
    }
  }
  return 0;
}

B-Inversion

知识点

  • 逆序对个数。基本思路是二重循环,时间复杂度 O ( n 2 ) O(n^2) O(n2)会超时。因此选择采用归并排序的形式来分治优化,达到 O ( n log ⁡ n ) O(n\log n) O(nlogn)的时间复杂度

拓展知识

这一题其实可以采用线段树或者离散化树状数组等高级算法知识,但是时间可能并不优于分治策略

相关链接

https://leetcode.cn/problems/shu-zu-zhong-de-ni-xu-dui-lcof/solutions/216984/shu-zu-zhong-de-ni-xu-dui-by-leetcode-solution/

https://oi-wiki.org/ds/seg/#%E7%BA%BF%E6%AE%B5%E6%A0%91

相关链接

力扣原题:https://leetcode.cn/problems/shu-zu-zhong-de-ni-xu-dui-lcof/description/

代码实现

C++分治策略

#include <iostream>
#include <vector>
using namespace std;
void mergeSort(vector<int> &nums, int l, int r, unsigned long long &ans);
void merge(vector<int> &nums, int l, int m, int r, unsigned long long &ans);
int main() {
  int n;
  cin >> n;
  vector<int> nums(n);
  for (int i = 0; i < n; i++) {
    int tmp;
    cin >> tmp;
    nums[i] = tmp;
  }
  unsigned long long ans = 0;
  mergeSort(nums, 0, n - 1, ans);
  cout << ans << endl;
}
void merge(vector<int> &nums, int l, int m, int r, unsigned long long &ans) {
  vector<int> tmp1(m - l + 1), tmp2(r - m);
  for (int i = l; i <= m; i++) {
    tmp1[i - l] = nums[i];
  }
  for (int i = m + 1; i <= r; i++) {
    tmp2[i - m - 1] = nums[i];
  }
  int i = 0, j = 0;
  while (i < m - l + 1 && j < r - m) {
    if (tmp1[i] <= tmp2[j]) {
      nums[l + i + j] = tmp1[i];
      i++;
    } else {
      nums[l + i + j] = tmp2[j];
      j++;
      ans += m - l + 1 - i;
    }
  }
  while (i < m - l + 1) {
    nums[l + i + j] = tmp1[i];
    i++;
  }
  while (j < r - m) {
    nums[l + i + j] = tmp2[j];
    j++;
  }
}
void mergeSort(vector<int> &nums, int l, int r, unsigned long long &ans) {
  if (l < r) {
    int m = l + (r - l) / 2;
    mergeSort(nums, l, m, ans);
    mergeSort(nums, m + 1, r, ans);
    merge(nums, l, m, r, ans);
  }
}

Java线段树

import java.util.*;

public class Main {
  static long[] d;

  public static void main(String[] args) {
    Scanner in = new Scanner(System.in);
    int n = 0;
    n = in.nextInt();
    long ret = 0;
    d = new long[n * 4];
    for (int i = 0; i < n; i++) {
      int x = in.nextInt();
      add(1, 1, n + 1, x);
      ret += query(1, 1, n + 1, x + 1, n);
    }
    System.out.println(ret);
  }

  static void add(int p, int l, int r, int num) {
    if (l == r) {
      d[p]++;
      return;
    }
    int mid = l + ((r - l) >> 1);
    if (num <= mid) {
      add(p * 2, l, mid, num);
    } else {
      add(p * 2 + 1, mid + 1, r, num);
    }
    d[p] = d[p * 2] + d[p * 2 + 1];
  }

  static long query(int p, int l, int r, int L, int R) {
    if (L <= l && R >= r) {
      return d[p];
    }
    long sum = 0;
    int mid = l + ((r - l) >> 1);
    if (L <= mid) {
      sum += query(p * 2, l, mid, L, R);
    }
    if (R > mid) {
      sum += query(p * 2 + 1, mid + 1, r, L, R);
    }
    return sum;
  }
}

C-Points

知识点

  • 不太好想的分治策略。对点按x坐标排序,然后分成左右两份分治,得到左边的最小距离和右边的最小距离,由此得到两边的最小距离,归并过程中,我们只要考虑跨越中间点的最小距离即可,从中间开始向两端遍历将点压入栈中,发现距离大于之前记录的最小距离就可停止遍历,然后将栈中的点按y排序并计算距离
  • 优化技巧:我们能不能先不开方,最后再开方(开方的性能损耗非常大)?可惜我试了很久没成功。

相关链接

洛谷原题:https://www.luogu.com.cn/problem/P1429

代码实现

c++

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <iostream>
#include <vector>
#define int long long
using namespace std;
inline double length(vector<int> p1, vector<int> p2);
double divide_conquer(vector<vector<int>> &points, int l, int r);
signed main() {
  int n;
  cin >> n;
  vector<vector<int>> points(n, vector<int>(2));
  for (int i = 0; i < n; i++) {
    cin >> points[i][0] >> points[i][1];
  }
  sort(points.begin(), points.end());
  printf("%.4f", divide_conquer(points, 0, n - 1));
}
inline double length(vector<int> p1, vector<int> p2) {
  return sqrt((p1[0] - p2[0]) * (p1[0] - p2[0]) + (p1[1] - p2[1]) * (p1[1] - p2[1]));
}
double divide_conquer(vector<vector<int>> &points, int l, int r) {
  if (l == r - 1) {
    return length(points[l], points[r]);
  }
  if (l < r) {
    int m = l + (r - l) / 2;
    double l1 = divide_conquer(points, l, m);
    double l2 = divide_conquer(points, m + 1, r);
    double ans = min(l1, l2);
    vector<vector<int>> tmp{points[m]};
    int midX = points[m][0];
    for (int i = m - 1;
         i >= l && midX - points[i][0] < ans;
         tmp.push_back(points[i]), i--)
      ;
    for (int i = m + 1;
         i <= r && points[i][0] - midX < ans;
         tmp.push_back(points[i]), i++)
      ;
    sort(tmp.begin(), tmp.end(),
         [](vector<int> &p1, vector<int> &p2) { return p1[1] < p2[1]; });
    int tmpN = tmp.size();
    for (int i = 0; i < tmpN; i++) {
      for (int j = i + 1;
           j < tmpN && tmp[j][1] - tmp[i][1] < ans;
           ans = min(ans, length(tmp[i], tmp[j])), j++)
        ;
    }
    return ans;
  }
  return 0x7fffffff;
}

D-Strassen

知识点

  • Strassen公式
  • 优化技巧:在矩阵计算的基本方法中,我们一般会按i-j-k的方式去遍历, O ( n 3 ) O(n^3) O(n3)显然会超时,但如果我们采用i-k-j的方式去遍历呢?提示:可以减少向内存中读取数据的次数,极大优化时间,与Strassen算法不相上下

代码实现

c++ Strassen算法

#include <iostream>
#include <vector>
#define int long long
using namespace std;

inline vector<vector<int> > sub_matrix(const vector<vector<int> > &m1, int x1, int y1,
                                       const vector<vector<int> > &m2, int x2, int y2, int nn);

vector<vector<int> > mul_matrix(const vector<vector<int> > &m1, int x1, int y1,
                                const vector<vector<int> > &m2, int x2, int y2, int nn);

inline vector<vector<int> > add_matrix(const vector<vector<int> > &m1, int x1, int y1,
                                       const vector<vector<int> > &m2, int x2, int y2, int nn);

signed main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(nullptr);
  int n;
  cin >> n;
  vector a(n, vector<int>(n));
  vector b(n, vector<int>(n));
  for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
      cin >> a[i][j];
    }
  }
  for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
      cin >> b[i][j];
    }
  }
  for (auto res = mul_matrix(a, 0, 0, b, 0, 0, n); const auto &row: res) {
    for (const auto v: row) {
      cout << v << " ";
    }
    cout << endl;
  }
}

inline vector<vector<int> > sub_matrix(const vector<vector<int> > &m1, const int x1, const int y1,
                                       const vector<vector<int> > &m2, const int x2, const int y2,
                                       const int nn) {
  vector<vector<int> > ret(nn, vector<int>(nn));
  for (int i = 0; i < nn; i++) {
    for (int j = 0; j < nn; j++) {
      ret[i][j] = m1[x1 + i][y1 + j] - m2[x2 + i][y2 + j];
    }
  }
  return ret;
}

inline vector<vector<int> > add_matrix(const vector<vector<int> > &m1, const int x1, const int y1,
                                       const vector<vector<int> > &m2, const int x2, const int y2,
                                       const int nn) {
  vector<vector<int> > ret(nn, vector<int>(nn));
  for (int i = 0; i < nn; i++) {
    for (int j = 0; j < nn; j++) {
      ret[i][j] = m1[x1 + i][y1 + j] + m2[x2 + i][y2 + j];
    }
  }
  return ret;
}

vector<vector<int> > mul_matrix(const vector<vector<int> > &m1, const int x1, const int y1,
                                const vector<vector<int> > &m2, const int x2, const int y2,
                                const int nn) {
  if (nn <= 128) {
    vector ret(nn, vector<int>(nn));
    for (int i = 0; i < nn; ++i) {
      for (int j = 0; j < nn; ++j) {
        int tmp = 0;
        for (int k = 0; k < nn; ++k) {
          tmp += m1[x1 + i][y1 + k] * m2[x2 + k][y2 + j];
        }
        ret[i][j] = tmp;
      }
    }
    return ret;
  }
  // divide
  const int tn = nn / 2;
  auto tmp = sub_matrix(m2, x2, y2 + tn, m2, x2 + tn, y2 + tn, tn);
  const auto p1 = mul_matrix(m1, x1, y1, tmp, 0, 0, tn);
  tmp = add_matrix(m1, x1, y1, m1, x1, y1 + tn, tn);
  const auto p2 = mul_matrix(tmp, 0, 0, m2, x2 + tn, y2 + tn, tn);
  tmp = add_matrix(m1, x1 + tn, y1, m1, x1 + tn, y1 + tn, tn);
  const auto p3 = mul_matrix(tmp, 0, 0, m2, x2, y2, tn);
  tmp = sub_matrix(m2, x2 + tn, y2, m2, x2, y2, tn);
  const auto p4 = mul_matrix(m1, x1 + tn, y1 + tn, tmp, 0, 0, tn);
  tmp = add_matrix(m1, x1, y1, m1, x1 + tn, y1 + tn, tn);
  auto tmp2 = add_matrix(m2, x2, y2, m2, x2 + tn, y2 + tn, tn);
  const auto p5 = mul_matrix(tmp, 0, 0, tmp2, 0, 0, tn);
  tmp = sub_matrix(m1, x1, y1 + tn, m1, x1 + tn, y1 + tn, tn);
  tmp2 = add_matrix(m2, x2 + tn, y2, m2, x2 + tn, y2 + tn, tn);
  const auto p6 = mul_matrix(tmp, 0, 0, tmp2, 0, 0, tn);
  tmp = sub_matrix(m1, x1, y1, m1, x1 + tn, y1, tn);
  tmp2 = add_matrix(m2, x2, y2, m2, x2, y2 + tn, tn);
  const auto p7 = mul_matrix(tmp, 0, 0, tmp2, 0, 0, tn);
  // merge
  vector ret(nn, vector<int>(nn));
  for (int i = 0; i < tn; ++i) {
    for (int j = 0; j < tn; ++j) {
      ret[i][j] = p5[i][j] + p4[i][j] - p2[i][j] + p6[i][j];
    }
  }
  for (int i = 0; i < tn; ++i) {
    for (int j = tn; j < nn; ++j) {
      ret[i][j] = p1[i][j - tn] + p2[i][j - tn];
    }
  }
  for (int i = tn; i < nn; ++i) {
    for (int j = 0; j < tn; ++j) {
      ret[i][j] = p3[i - tn][j] + p4[i - tn][j];
    }
  }
  for (int i = tn; i < nn; ++i) {
    for (int j = tn; j < nn; ++j) {
      ret[i][j] = p1[i - tn][j - tn] + p5[i - tn][j - tn] - p3[i - tn][j - tn] -
                  p7[i - tn][j - tn];
    }
  }
  return ret;
}

Java 暴力

import java.util.*;
import java.io.*;

public class Main {
  public static void main(String[] args) {
    Scanner in = new Scanner(System.in);
    int n = 0;
    n = in.nextInt();
    int[][] a = new int[n][n];
    for (int i = 0; i < n; i++) {
      for (int j = 0; j < n; j++) {
        a[i][j] = in.nextInt();
      }
    }
    int[][] b = new int[n][n];
    for (int i = 0; i < n; i++) {
      for (int j = 0; j < n; j++) {
        b[i][j] = in.nextInt();
      }
    }
    int[][] c = new int[n][n];
    for (int i = 0; i < n; i++) {
      for (int k = 0; k < n; k++) {
        int tmp = a[i][k];
        for (int j = 0; j < n; j++) {
          c[i][j] += tmp * b[k][j];
        }
      }
    }
    PrintWriter out = new PrintWriter(System.out);
    for (int i = 0; i < n; i++) {
      for (int j = 0; j < n; j++) {
        out.print(c[i][j]);
        out.print(" ");
      }
      out.println();
    }
    out.flush();
    in.close();
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值