逆序对问题小计-爆搜,分治(Poj 1007,洛谷 P1908 逆序对)

求逆序对是一个经常遇到的问题,很久之前做过一道,今天又遇到一个类似的,用了大半个小时才把分治写好,写篇文章记录一下。

典型题-Poj 1007

逆序对问题小计-爆搜,分治(Poj 1007,洛谷 P1908 逆序对)_第1张图片

爆搜

这道题由于数据范围很小,简单的暴力搜索也可以AC。对字符串中每一个字符,都要遍历其后的所有字符,统计逆序对, O ( n 2 ) O(n^2) O(n2)

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;

#define ll int
#define p pair

ll n, m;
char str[105][55];
vector<p> v;
//第row行元素有多少inversion
void measure(ll row) {
	ll sum = 0, cnt;
	for (ll i = 0; i < n; i++) {
		cnt = 0;
		for (ll j = i + 1; j < n; j++) {
			if (str[row][i] > str[row][j]) cnt++;
		}
		sum += cnt;
	}
	string s(str[row]);
	v.push_back(make_pair(sum, s));
}

int main() {
	cin >> n >> m;
	for (ll i = 0; i < m; i++) cin >> str[i];
	for (ll i = 0; i < m; i++) measure(i);
	sort(v.begin(), v.end());
	for (unsigned i = 0; i < v.size(); i++)cout << v[i].second.c_str() << endl;
}

分治-归并排序解法

最稳妥的方法无疑是分而治之的思想,如果我们想要将一个序列排成从小到大有序的,那么每次划分后合并时左右子区间都是从小到大排好序的,我们只需要统计右边区间每一个数分别会与左边区间产生多少逆序对即可。用整数举一个例子

//在某个时候,左区间:  5 6 7  下标为i
//           右区间:  1 2 9  下标为j
//          
//这个时候我们进行合并:
//step 1:由于 5>1,所以产生了逆序对,这里,我们发现,左区间所有还没有被合并的数都比 1 大,所以1与左区间所有元素共产生了 3 个逆序对(即tot_numleft-i+1对),统计答案并合并 1 
//step 2:由于 5>2,由上产生了3对逆序对,统计答案并合并 2
//step 3:由于 5<9, 没有逆序对产生,右区间下标 j++
//step 4:由于 6<9, 没有逆序对产生,右区间下标 j++
//step 5:由于 7<9, 没有逆序对产生,右区间下标 j++
//step 6:由于右区间已经结束,正常执行合并左区间剩余,结束

//PS: tot_numleft=3,即左区间总元素个数

对一个字符串而言,复杂度是 O ( n log ⁡ ( n ) ) O(n\log(n)) O(nlog(n)),在数据范围大的时候很起作用。

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;

#define ll int
#define p pair

ll n, m;
char str[105][55], tmp[105], tt[105];
vector<p> v;

//第row行元素有多少inversion
ll measure(ll row, ll l, ll r) {
	if (r <= l) return 0;
	ll mid = (l + r) >> 1;
	ll d1 = measure(row, l, mid);
	ll d2 = measure(row, mid + 1, r);
	ll k = l, lt = l, rt = mid + 1, res = d1 + d2;
	while (lt <= mid && rt <= r) {
		if (tt[lt] <= tt[rt]) tmp[k++] = tt[lt++];
		else if (tt[lt] > tt[rt]) {
			res += (mid - lt + 1); tmp[k++] = tt[rt++];
		}
	}
	while (lt <= mid) tmp[k++] = tt[lt++];
	while (rt <= r) tmp[k++] = tt[rt++];
	for (ll i = l; i <= r; i++)tt[i] = tmp[i];
	return res;
}

int main() {
	cin >> n >> m;
	for (ll i = 0; i < m; i++) cin >> str[i];
	for (ll i = 0; i < m; i++) {
		for (ll j = 0; j < n; j++) tt[j] = str[i][j]; string s(str[i]);
		v.push_back(p(measure(i, 0, n - 1), s));
	}
	sort(v.begin(), v.end());
	for (unsigned i = 0; i < v.size(); i++)cout << v[i].second.c_str() << endl;
}

你可能感兴趣的:(ACM杂项)