BZOJ3990/SDOI2015 排序 搜索

版权声明:本文为DyingShu原创文章,转载请注明出处哦。 https://blog.csdn.net/DyingShu/article/details/82805884

传送门

暴力出正解系列,直接开搜

注意到操作顺序不影响答案,因此从小到大安排,最后统计答案时增加交换次数的阶乘就行了
如果一种状态已经开始选择第 i i 种操作,那么必须保证所有长度为 2 i 1 2^{i - 1} 的块(从第一位开始,每块首尾相接)内部已经排好序了。而如果有2个以上长度为 2 i 2^{i} 的内部没有排好序的块,就不能通过一次操作将其排好序。而在其之后的操作也不能把没有排好序的块复原

其实第一个剪枝就能得75分了qwq

#include<cstdio>
#include<algorithm>
using namespace std;
int n, Ans;
int a[1 << 13];
int fac[13];

inline int read(){
	int k = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9'){k = k*10 + ch - '0'; ch = getchar();}
	return k * f;
}

inline bool Check(int t){
	if(!t) return true;
	int Len = (1 << (t - 1));
	for(int i = 1; i <= (1 << n); i += Len * 2)
		if(a[i] + Len != a[i + Len]) return false;
	return true;
}

inline void Swap(int l1, int l2, int Len){
	for(int i = 0; i < Len; i++){
		swap(a[l1 + i], a[l2 + i]);
	}
}

void dfs(int t, int num){
	if(!Check(t)) return;
	if(t == n){
		Ans += fac[num];
		return;
	}
	int tot = 0, w[10] = {0};
	int Len = (1 << t);
	for(int i = 1; i <= (1 << n); i += Len * 2){
		if(a[i] + Len != a[i + Len]){
			if(tot == 4) return; //交换不过来了 
			w[++tot] = i;
			w[++tot] = i + Len;
		}
	}
	
	for(int i = 1; i <= tot; i++){
		for(int j = i + 1; j <= tot; j++){
			Swap(w[i], w[j], Len);
			dfs(t + 1, num + 1);
			Swap(w[i], w[j], Len);
		}
	}

	dfs(t + 1, num); //不做 
}

int main(){
	n = read();
	for(int i = 1; i <= (1 << n); i++){
		a[i] = read();
	}
	fac[0] = 1;
	for(int i = 1; i <= n; i++){
		fac[i] = fac[i - 1] * i;
//		printf("%d ", fac[i]);
	}
	dfs(0, 0);
	printf("%d", Ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/DyingShu/article/details/82805884