首页 > 编程语言 >分治算法总结(未完结)

分治算法总结(未完结)

时间:2023-03-18 20:56:10浏览次数:55  
标签:未完结 nums int 分治 mid 算法 数组 区间 逆序

分治思想:将一个问题分解成若干个结构与原问题相同的子问题。(划分子问题 + 后处理)

一、经典问题


1. 最大子段和

思路: 计算左边的最大子段和、右边的最大子段和以及跨越中界的最大子段和,然后求最大值。
每次划分左右相当于将问题二分,最多划分logn次,跨越中界求最大子段和时间复杂度是O(n),因此可得:
总体时间复杂度为O(nlogn)

int a[N];
int solve(int L, int R) {
	if (L == R) return a[L];
	int mid = (L + R) >> 1;
	int L_ans = solve(L, mid), R_ans = solve(mid+1, R);
	int maxsuf = -INF, maxpre = -INF, suf = 0, pre = 0;
	for (int i = mid; i >= L; --i) {
	 suf += a[i]; // 求最大后缀和
	 maxsuf = max(maxsuf, suf);
}
	for (int i = mid + 1; i <= R; ++i) {
	 pre += a[i];
	 maxpre = max(maxpre, pre); //求最大前缀和
}
	 return max(max(L_ans, R_ans), maxsuf + maxpre);
}

优化思路:划分后的每层都有重复计算部分,可以把结果用一个数组保存下来 >> DP

2. 归并排序

将一个乱序的数组分为左右两个部分,问题二分后,只需要执行将两个数组合并这一步操作, 使用双指针可实现O(n)的合并。

int a[N], tmp[N];
void merge_sort(int l, int r) {
	if (l >= r) return;
	int mid = (l + r) >> 1;
	merge_sort(l, mid);
	merge_sort(mid+1, r);
	int i = l , j = mid + 1, k = l;
	while(i <= mid && j <= r) {
		if (a[i] < a[j]) tmp[k++] = a[i++];
		else tmp[k++] = a[j++];
	}
	while(i <= mid) tmp[k++] = a[i++];
	while(j <= r) tmp[k++] = a[j++];
	for (int i = l; i <= r; ++i) a[i] = tmp[i];
}



二、CDQ分治


CDQ分治:适用于偏序问题的离线算法。
主要思想:在分治问题中,将问题分成两个子问题,将左区间答案、右区间答案、右区间对左区间的贡献或者左区间对右区间的贡献进行合并后得到总问题的答案。
可处理的问题:求两种属性或者多种属性的元素对满足某种特定关系的数目(例如:下标满足一种特征,值满足一种特征)

2.1 求逆序对数

思路:分为左右两个区间,左边区间的下标都是小于右边区间的下标,(下标满足顺序关系),左右区间排好序后,归并时判断是否是逆序对即可
归并过程:
i < j, a[i] <= a[j] 则 左区间元素并入,左区间i号元素对右区间逆序对贡献为0
i < j, a[i] > a[j] 则 右区间元素并入,右区间j号元素对左区间的逆序对贡献为mid-i+1。
最后计算总逆序对贡献

int a[N], tmp[N];
ll CDQ(int l, int r) {
	if (l >= r) return 0;
	int mid = (l+r) >> 1;
	ll ans = CDQ(l ,mid) + CDQ(mid+1, r);
	int i = 1, j = mid + 1, k = l;
	while (i <= mid && j <= r) {
	if (a[i] <= a[j]) tmp[k++] = a[i++];
	else {
			tmp[k++] = a[j++];
			ans += mid - i + 1;
		}
	}
	while(i <= mid) tmp[k++] = a[i++]
	while(j <= mid) tmp[k++] = a[j++];
	for (int i = l; i <= r; ++i)
		a[i] = tmp[i];
	return ans;
}

可用树状数组求逆序对数

树状数组是用于维护前缀和的数据结构,有update和query两个操作,这两个操作的复杂度均为O(logn),这个问题中可以在值域上建数,类似于桶,每个桶里装这个元素的个数。
树状数组详细讲解可参考:leetcode: 有序数组、归并排序、树状数组和线段树的实现及注释

class Solution {
public:
    int reversePairs(vector<int>& nums) {
        int n = nums.size();
        vector<int> v(nums);
        sort(v.begin(), v.end());
        v.erase(unique(v.begin(), v.end()), v.end()); // 去重
        int m = v.size(); // 去重后的长度
        BIT tree(m);
        int ans = 0;
        for (int i = 0; i < n; i++) {
            nums[i] = lower_bound(v.begin(), v.end(), nums[i]) - v.begin() + 1;
        } //离散化,按大小顺序给每个值编号,不影响最后的结果
        for (int i = 0; i < n; i++) {
            ans += i - tree.query(nums[i]); // i - query表示大于nums[i]的个数,即逆序个数
            tree.update(nums[i], 1);
        }
        return ans;
    }

	class BIT { // 树状数组 BIT (Binary Indexed Tree)查询,修改时间复杂度均为O(logn)
	public:
		BIT(int n){
			this->n = n;
			c.resize(n+1);
		}
		int lowbit(int x) {
			return x & -x;
		}
		void update(int x, int v) { // 在位置x处增加v
		for (int i = x; i < n+1; i += lowbit(i)) c[i] += v;
		}
		int query(int x) {
			int res = 0;
			//求前缀和, 这个问题中是求小于等于x的数目,因此上面的i - query(x)就是前i个中大于x的数目,即逆序对数
			for (int i = x; i > 0; i -= lowbit(i))
				res += c[i];
			return res;
		}
	private:
		int n;
		vector<int> c;
	};
};

可用线段树代替树状数组来求逆序对数:

线段树是一种二叉树,用于解决区间查询问题,例如区间最小值、区间最大值、区间和等问题。线段树的每一个节点表示一个区间,包括区间的左右端点和一些其他信息,例如区间的最小值、最大值、和等。线段树的根节点表示整个区间,每个节点的左儿子表示左区间,右儿子表示右区间,对于需要查询的区间,通过递归地访问线段树的节点,可以快速计算出区间的最小值、最大值、和等。

类似于树状数组,只是会比树状数组更大,依然查询区间和,用来求每个元素对求逆序对数的贡献,相加后得到总的逆序对数。


class Solution {
public:
    int reversePairs(vector<int>& nums) {
        int n = nums.size();
        vector<int> v(nums);
        sort(v.begin(), v.end());
        v.erase(unique(v.begin(), v.end()), v.end());
        int m = v.size();
        SegmentTree st(m);
        int ans = 0;
        for (int i = 0; i < n; i++) {
            nums[i] = lower_bound(v.begin(), v.end(), nums[i]) - v.begin() + 1;
        } //离散化
        for (int i = 0; i < n; i++) {
            ans += st.calcSum(nums[i]+1, m); // 计算之前比它大的元素,逆序
            st.add(nums[i], 1);
        }
        return ans;
    }

class SegmentTree {
public:
	SegmentTree(int n) {
		this->n = n;
	    this->tree.resize(2*n+1);
	}
	void add(int i, int v) { // 从叶子节点一直到根节点沿路+v,更新区间和
		i += n;
		while(i > 0) {
            tree[i] += v;
            i = i >> 1;
        }
    }
	int calcSum(int l, int r) {
		l += n, r += n; // 从叶子节点开始计算
		int sum = 0;
		while(l <= r) {
			if(l&1) sum += tree[l++]; // 右子节点,不能直接除以2,加上这个单节点后要跨一步
			if(!(r&1)) sum += tree[r--]; // 左子节点,不能直接除以2,加上这个单节点要跨一步
			l = l >> 1;
			r = r >> 1;
		}
	return sum;
	}
private:
    int n;
    vector<int> tree;
};
};

2.2 三维偏序问题

问题描述:给定n个元素,每个元素有三个属性a、b、c,最大属性值为k,f(i)表示对于每个i (1 ≤ i ≤ n ) ,有多少个j满足j≠i且a_j ≤ a_i, b_j ≤ b_i, c_j ≤ c_i,求对于f的每一个可能取值,有多少个i满足要求。
思路:用CDQ嵌套CDQ方式或者CDQ嵌套树状数组的方式解决、

待续

标签:未完结,nums,int,分治,mid,算法,数组,区间,逆序
From: https://www.cnblogs.com/raiuny/p/17231472.html

相关文章

  • 算法刷题记录
    http://acm.hdu.edu.cn/showproblem.php?pid=1094#include<iostream>#include<vector>intmain(){usingnamespacestd;vector<int>vecrow;......
  • 代码随想录算法训练营Day46 动态规划
    代码随想录算法训练营代码随想录算法训练营Day46动态规划|● 139.单词拆分关于多重背包,你该了解这些!背包问题总结篇!139.单词拆分题目链接:139.单词拆分给定一......
  • 算法 -- 分割两个字符串得到回文串
    分割两个字符串得到回文串提示中等114相关企业给你两个字符串a和b,它们长度相同。请你选择一个下标,将两个字符串都在相同的下标分割开。由a可以得到两个字符......
  • 【基础算法】简单排序-冒泡排序
    【基础算法】简单排序-冒泡排序BubbleSortisthesimplestsortingalgorithmthatworksbyrepeatlyswappingtheadjacentelementsiftheyareinthewrongorde......
  • 常用排序算法
    1.冒泡排序冒泡排序:相邻的数两两比较,小的放前面,大的放后面。冒泡排序(BubbleSort)也是一种简单直观的排序算法。它重复的遍历过要排序的数列,一次比较相邻的两个元素,如果......
  • 希尔排序、快速排序、KMP算法
    希尔排序背景对N个样本进行排序,顺序为从小至大步骤第一步:对样本不断确定分组的间隔gap,确定分组次数X-》对应第一个循环第一次gap=N/2;第二次gap=gap/2;......
  • Tarjan算法详解
    Tarjan算法介绍Tarjan算法是用于在有向图中求强连通分量的算法这里给出强连通分量的定义:有向图中一个最大的图,使这个图中每个两点都能够互相到达。这个最大的图称为强连......
  • 算法随想Day41【动态规划】| LC139-单词拆分
    LC139.单词拆分dp[i]含义:字符串长度为i的话,dp[i]为true,表示可以拆分为一个或多个在字典中出现的单词遍历顺序:如题说,是拆分为一个或多个在字典中出现的单词,所以这是完......
  • 层次聚类算法
    动动发财的小手,点个赞吧!层次聚类是一种构建聚类层次结构的聚类算法。该算法从分配给它们自己的集群的所有数据点开始。然后将两个最近的集群合并到同一个集群中。最后,当......
  • 漫画:什么是选择排序算法?
    选择排序是一种简单直观的算法,今天我们聊聊选择排序的思想,代码以及复杂度排序思想一天,小一尘和师傅下山去了,在集市中路经一个水果摊,只见水果摊上摆着色泽基本相同但大......