[LOJ6043]蛐蛐国的修墙方案

89 篇文章 0 订阅
7 篇文章 0 订阅

题目

传送门 to LOJ

思路

⟨ i , p i ⟩ \lang i,p_i\rang i,pi 这些边构成很多环,环上相邻两点不同。说白了,每个环只有两种情况。

并不是高级算法,就是枚举。一开始以为是玄学搜索,没想到它的复杂度是严格的……

只需要注意到一点:二元环一定是靠左的为左括号,另一个为右括号。这是显然的,因为合法括号序列等价于任意前缀有 ( ( (左括号数量 ) ≥ ( )\ge( )(右括号数量 ) ) ) ,而这样固定二元环是没有任何负面作用的。

没有三元环,不然无解。题目中没有给出是否保证有解,这是题目描述的纰漏。但我告诉你,确实都有解,所以没有奇环。于是剩下的最小是四元环,所以复杂度 Θ ( 2 n 4 ) \mathcal \Theta(2^{\frac{n}{4}}) Θ(24n) ,而且可行性剪枝能够剪去很多。这是严格的复杂度啊!

代码

#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdlib>
using namespace std;
typedef long long int_;
inline int readint(){
	int a = 0; char c = getchar(), f = 1;
	for(; c<'0'||c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}
template < typename T >
inline void getMax(T &a,const T &b){
	(a < b) ? (a = b) : 0;
}
template < typename T >
inline void getMin(T &a,const T &b){
	(b < a) ? (a = b) : 0;
}

const int MaxN = 105;
int n; // 基本信息:点的数量
int bel[MaxN]; // 属于哪一组
int k[MaxN]; // 对应于 bel 的系数
int tmp[MaxN]; // 每一组的系数
int val[MaxN]; // 目前的选择
bool check(){
	for(int i=1,now=0; i<=n; ++i){
		if(i == bel[i]) tmp[i] = 0;
		val[i] = k[i]*val[bel[i]];
		if(val[i] != 0){
			now += val[i];
			if(now < 0) return false;
			continue; // 不管下面的
		}
		if(k[i]*tmp[bel[i]] < 0){ // 异号
			if((-- now) < 0) // 绝对值变小
				return false; // 判断效果
		} else ++ now; // 否则增大
		tmp[bel[i]] += k[i];
	}
	return true;
}
int now; // 整体作用于 dfs
void dfs(int t){
	if(t == n+1){
		for(int i=1; i<=n; ++i)
			putchar(val[i] == 1 ? '(' : ')');
		exit(0); // 直接 go away!
	}
	if(val[t]){
		now += val[t], dfs(t+1);
		now -= val[t]; return ;
	}
	val[t] = 1;
	if(check()){
		now += val[t], dfs(t+1);
		now -= val[t];
	}
	val[t] = -val[t]; // 否则换花样
	if(check()){
		now += val[t], dfs(t+1);
		now -= val[t];
	}
	val[t] = 0; // 不知道怎么办才好
}

int p[MaxN];
int main(){
	srand(5201314);
	n = readint();
	for(int i=1; i<=n; ++i)
		p[i] = readint();
	for(int i=1; i<=n; ++i){
		if(bel[i]) continue;
		bel[i] = i, k[i] = 1;
		for(int j=i; p[j]!=i; j=p[j]){
			bel[p[j]] = i;
			k[p[j]] = -k[j];
		}
		if(p[p[i]] == i) // 二元环
			val[i] = 1, val[p[i]] = -1;
	}
	dfs(1); // 减少参数数量
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值