算法设计分析HW3 分治上机三题
冯浩然 1600013009
1
Ultra-QuikSort2
- 实际是求逆序对个数。尽管明知道这是考分治要卡时间,但第一次还是作死地试了试 O(n2) O ( n 2 ) 的冒泡,然后还不死心的试了试带swap标记的优化版冒泡,果然都死了。
- 于是乖乖使用分治。算法实质就是
mergesort
,但需要对merge
函数动一些手脚。在对数组 A,B A , B 进行归并时,此时已经归并了 i i 个元素。如果发现,就说明 A[index1] A [ i n d e x 1 ] 与他后面的 B[0] B [ 0 ] 到 B[index2] B [ i n d e x 2 ] 的元素构成 (index2−i) ( i n d e x 2 − i ) 个逆序对那么就给 rev_cnt r e v _ c n t 变量加上 (index2−i) ( i n d e x 2 − i ) - 复杂度 O(nlogn) O ( n l o g n )
- 代码如下
#include <iostream>
#include <cstdio>
using namespace std;
#define MAXN 500000 + 10
typedef long long ll;
ll n, rev_cnt;
ll c[MAXN], c_tmp[MAXN];
ll merge(int l, int r, int mid)
{
ll cnt = 0;
ll i, j, index1, index2;
for (j = l; j <= r; j++)
c_tmp[j] = c[j];
index1 = l;
index2 = mid + 1;
i = l;
while (index1 <= mid && index2 <= r){
if (c_tmp[index1] <= c_tmp[index2])
c[i++] = c_tmp[index1++];
else{
rev_cnt += index2 - i;
c[i++] = c_tmp[index2++];
}
}
while(index1 <= mid)
c[i++] = c_tmp[index1++];
while(index2 <= r)
c[i++] = c_tmp[index2++];
return cnt;
}
ll merge_sort(int l, int r)
{
ll mid, cnt = 0;
if (l < r){
mid = (l + r) / 2;
cnt += merge_sort(l, mid);
cnt += merge_sort(mid + 1, r);
cnt += merge(l, r, mid);
}
return cnt;
}
int main()
{
while (cin >> n){
if (n == 0)
break;
for (int i = 0; i < n; i++){
scanf(" %lld", &c[i]);
}
rev_cnt = 0;
merge_sort(0, n - 1);
//rev_cnt = merge_sort(0, n - 1);
/*
int rev_cnt = 0;
bool no_swap;
for (int i = 0; i < n - 1; i++){
no_swap = true;
for (int j = n - 1; j >= i; j--){
if (c[j] < c[j - 1]){
int tmp = c[j];
c[j] = c[j - 1];
c[j - 1] = tmp;
no_swap = false;
rev_cnt++;
}
}
}
*/
printf("%lld\n", rev_cnt);
}
return 0;
}
2
最近点对问题
- 课上老师详细分析过此题的算法,只要复现即可
- 具体操作为将点集对半划分-两边递归各算各的-算一下分别在两边的离得比较近的点。需要注意的是在找分别在两边的点对时,可以以距离mid距离不大于 δ δ 为界。
- 复杂度 O(nlogn) O ( n l o g n )
- 代码如下
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <math.h>
using namespace std;
#define MAXN 200000 + 10
typedef long long ll;
int N;
struct point
{
ll x, y;
bool operator < (const point &b){
return x < b.x;
}
}P[MAXN];
double dis(const point &a, const point &b){
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
double divide_findmin(int l, int r)
{
if (r - l == 1){
return dis(P[l], P[r]);
}
if (r - l == 2){
double d[3];
d[0] = dis(P[l], P[l + 1]);
d[1] = dis(P[l], P[r]);
d[2] = dis(P[l + 1], P[r]);
return min(min(d[0], d[1]), d[2]);
}
int mid = (l + r) / 2;
double min_dis1, min_dis2, min_dis3, min_dis;
min_dis1 = divide_findmin(l, mid);
min_dis2 = divide_findmin(mid + 1, r);
min_dis = min(min_dis1, min_dis2);
/*find the min_dis near the line, which is \delta_3*/
point tmp[r - l];
int index1 = 0, index2 = 0;
for (int i = mid; (i >= l) && ((P[mid].x - P[i].x) < min_dis); i--)
tmp[index1++] = P[i];
for (int i = mid + 1; (i <= r) && ((P[i].x - P[mid].x) < min_dis); i++)
tmp[index1 + (index2++)] = P[i];
for (int i = 0; i < index1; i++)
for (int j = index1; j < index1 + index2; j++){
min_dis3 = dis(tmp[i], tmp[j]);
min_dis = min(min_dis, min_dis3);
}
return min_dis;
}
int main()
{
scanf(" %d", &N);
for (int i = 0; i < N; i++){
scanf(" %lld%lld", &P[i].x, &P[i].y);
}
sort(P, P + N);
double min_dis = divide_findmin(0, N - 1);
printf("%.3lf\n", min_dis);
return 0;
}
3
集合求交
上次的作业题了
先对 B B 排序,然后在其中顺次查找中的每个元素。如果找到,则说明在交集里含有这个元素
复杂度为 O(nlogm)=O(nloglogn) O ( n l o g m ) = O ( n l o g l o g n )
代码如下
#include <iostream> #include <cstdio> #include <algorithm> using namespace std; #define MAXN 100000 + 10 int a[MAXN], b[MAXN]; int c[MAXN]; int n, m; bool bin_search(int k, int l, int r) { while (l < r) { int mid = (l + r) / 2; if (b[mid] < k) l = mid + 1; else r = mid; } if (b[r] == k) return true; return false; } int main() { scanf(" %d%d", &n, &m); for (int i = 0; i < n; i++) scanf(" %d", &a[i]); for (int i = 0; i < m; i++) scanf(" %d", &b[i]); sort(b, b + m); int c_index = 0; for (int i = 0; i < n; i++) { //printf("End of search\n"); if (bin_search(a[i], 0, m - 1)) c[c_index++] = a[i]; } //sort(c, c + c_index); //for (int i = 0; i < c_index; i++) // printf("%d ", c[i]); //printf("End\n"); printf("%d\n", c_index); return 0; }