Acwing 180. 排书

给定 \(n\) 本书,编号为 1∼n。

在初始状态下,书是任意排列的。

在每一次操作中,可以抽取其中连续的一段,再把这段插入到其他某个位置。

我们的目标状态是把书按照 1∼n 的顺序依次排列。

求最少需要多少次操作。

解:

考虑每一步决策数量,当抽取长度为 \(len\) 的一段时,有 \(n - len+ 1\) 种抽法,对于每种抽法,有 \(n - len\) 种放法。另外,将某一段向前移动,等价于将跳过的那段向后移动,因此每种移动方式被算了两遍,所以每个状态总共的分支数量是:

\[\sum\limits_{len = 1}^n \cfrac{(n - len + 1) * (n - len)}{2} = \cfrac{15*14 + 14*13 + 13*12...+2*1}{2} = 560 \]

题目要求最多\(4\)次算出答案,那么就是\(560^4\),又由于\(IDA \,\, star\) 有估价函数,那么实际上数据量会更小。

对于估价函数:答案应该是\(1,2,3,4,5...,n\)的形式,所以每个数\(a[i]\)的的后边应该是\(a[i + 1]\),满足\(a[i] + 1 = a[i + 1]\),不满足就记录一下这种情况的数量\(cnt\),而我们每操作一次最多可以改变三个位置的地方,即\(l(当前段的左边),r(当前段的右边),k(插入的位置)\)三个位置的顺序,因此最多一次可以改变三个,即终点到当前状态下的预估距离,那么最优情况下我们操作一个序列改变三个,那么估价函数就设置为\(f(x) = \lceil \cfrac{cnt}{3} \rceil\),如果估价函数 \(f(x) = 0\),说明序列已经有序。

考虑\(IDA star\)\(A star\)

\(IDA star\)是迭代加深和估价函数结合版

// Problem: 排书
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/182/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

using namespace std;

const int N = 15;
int n;
int q[N];
int w[5][N];

int f() {
    int cnt = 0;
    for (int i = 0; i + 1 < n; i++) {
        if (q[i + 1] != q[i] + 1) cnt++;
    }
    return (cnt + 2) / 3;
}

bool dfs(int depth, int max_depth) {
    if (depth + f() > max_depth) return false;
    if (f() == 0) return true;

    for (int len = 1; len <= n; len++) {
        for (int l = 0; l + len - 1 < n; l++) {
            int r = l + len - 1;
            for (int k = r + 1; k < n; k++) { //把当前区间放到哪个位置上
                memcpy(w[depth], q, sizeof q);
                int y = l;
                for (int x = r + 1; x <= k; x++, y++) q[y] = w[depth][x];
                for (int x = l; x <= r; x++, y++) q[y] = w[depth][x];
                if (dfs(depth + 1, max_depth)) return true;
                memcpy(q, w[depth], sizeof q);
            }
        }
    }

    return false;
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        cin >> n;
        for (int i = 0; i < n; i++) cin >> q[i];
        int depth = 0;
        while (depth < 5 && !dfs(0, depth)) depth++;
        if (depth == 5) puts("5 or more");
        else cout << depth << endl;
    }
    return 0;
}

\(A star\)

注意:

在哈希表中嵌套结构体的时候需要重载相应的函数,例如哈希函数,所以这里不如直接自己实现哈希函数。

还要注意堆中嵌套结构体重载一下小于号的方式。

// Problem: 排书
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/182/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

using namespace std;

typedef unsigned long long ULL; 

const int N = 15;
int n, P = 17;
int w[5][N];

struct Node {
	int q[N], step, f;
	bool operator < (const Node &x) const{
		return f > x.f;
	}
}start;  

//在unordered_set中嵌套结构体还是要重写Hash函数,不如直接这样写

ULL getHash(Node x) {
	ULL res = 0;
	for (int i = 0; i < n; i++) {
		res = res * P + x.q[i];
	}
	return res;
}

int f(Node x) {
	int cnt = 0;
	for (int i = 0; i + 1 < n; i++) {
		if (x.q[i + 1] != x.q[i] + 1) cnt++;
	}
	return (cnt + 2) / 3;
}

int astar() {
        unordered_set<ULL> st;
	priority_queue<Node> heap;
	start.step = 0, start.f = f(start);
	heap.push(start); st.insert(getHash(start));
	while (heap.size()) {
		auto t = heap.top();
		heap.pop();
		
		if (t.f >= 5) return 5;
		if (f(t) == 0) return t.step;
		
		for (int len = 1; len <= n; len++) {
			for (int l = 0; l + len - 1 < n; l++) {
				int r = l + len - 1;
				for (int k = r + 1; k < n; k++) {
					Node v;
					for (int d = 0; d < n; d++) v.q[d] = t.q[d];
					int y = l;
					for (int x = r + 1; x <= k; x++, y++) v.q[x] = t.q[y];
					for (int x = l; x <= r; x++, y++) v.q[x] = t.q[y];
					if (st.count(getHash(v))) continue;
					st.insert(getHash(v));
					v.step = t.step + 1;
					v.f = v.step + f(v);
					heap.push(v); 
				}
			}
		}
	} 
	
	return 5;
}

int main() {
	int t;
	cin >> t;
	while (t--) {
		cin >> n;
		for (int i = 0; i < n; i++) cin >> start.q[i];
		int res = astar();
		if (res >= 5) puts("5 or more");
		else cout << res << endl;
	}
	return 0;
}



posted @ 2021-08-21 12:01  Xxaj5  阅读(47)  评论(0编辑  收藏  举报