2020 ICPC沈阳站 D - Journey to Un’Goro (爆搜+思维剪枝)

时间:2021-09-13
本文章向大家介绍2020 ICPC沈阳站 D - Journey to Un’Goro (爆搜+思维剪枝),主要包括2020 ICPC沈阳站 D - Journey to Un’Goro (爆搜+思维剪枝)使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

题意:
给定一个长度为\(n\)的序列,现在要求你对其进行染色,\(r\ or\ b\),两个颜色,要求染完色的序列,在其中选择尽量多对的下标\(1\le i\le j\le n\),使得序列\([i,j]\)中的\(r\)个数为奇数。
如果最多的答案对数对应了多种染色方案,如果不超过\(100\)个,请全部输出,否则输出前\(100\)个。

思路:
考虑另一个序列\(p_i\)表示前缀\([1,i]\)中,含有的\(r\)的个数,那么要是使得区间\([i,j]\)\(r\)的个数为奇数,即\(p_{i-1},p_j\)的奇偶性应该不同。
那么假如我们设\(p\)序列中,前缀中\(r\)为奇数的位置的数量为\(x\),前缀中\(r\)为偶数的位置的数量为\(y\),那么我们最多的对数就可以表示为\(x*y\),而且我们应当\(p_0\)这个值考虑到,即共\(n+1\)个位置,那么考虑什么时候能够使得\(x+y=n+1\),且\(x*y\)取到最大值,通过均值的不等式,就知道令\(x = (n + 1) / 2,y = ((n + 1) + 1) / 2\),或者二者相反即是答案。

我们令\(p_i = 0\),代表是偶数,\(p_i = 1\),代表是奇数。

爆搜枚举当前位置,是偶数还是奇数,通过\(x*y\)取得最大值的时候的个数的数量关系,剪枝掉个数大于一半的情况,考虑字典序从小到大输出,那么肯定是能先放\(b\)就放\(b\),而\(b\)是不会改变\(p\)序列的奇偶性的,所以要想先多放\(b\),那就优先考虑令当前位置和前一个位置的奇偶性相同,否则才考虑放置\(r\),所以我们只需要爆搜出一个代表奇偶的\(01\)序列,然后往对应位置上填\(b,r\)即可。

注意如果是爆搜枚举放置\(b,r\)那就错了,因为我们考虑的奇偶性,所以我们枚举的奇偶数量。

#include <bits/stdc++.h>

using namespace std;

#define pb push_back
#define eb emplace_back
#define MP make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define lson rt<<1
#define rson rt<<1|1
#define CLOSE std::ios::sync_with_stdio(false)
#define sz(x) (int)(x).size()
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-6;
const int N = 1e5 + 10;
int is_odd[N],n;

int cnt;
void dfs(int cnt0,int cnt1,int pos) {
	if(cnt1 > (n + 2) / 2 || cnt0 > (n + 2) / 2) return ;//可行性剪枝 大于这个数根本不可行
	if(pos == n + 1) {
		for(int i = 1;i <= n;i ++) {
			if(is_odd[i] == is_odd[i-1]) printf("b");
			else printf("r");
		}
		cnt++;
		printf("\n");
	}
	is_odd[pos] = is_odd[pos-1];
	if(is_odd[pos-1] == 0) {
		dfs(cnt0+1,cnt1,pos+1);
		if(cnt == 100) return ;
	}
	else {
		dfs(cnt0,cnt1+1,pos+1);
		if(cnt == 100) return ;
	}
	is_odd[pos] = is_odd[pos-1]^1;
	if(is_odd[pos-1] == 0) {
		dfs(cnt0,cnt1+1,pos+1);
		if(cnt == 100) return ;
	}
	else {
		dfs(cnt0+1,cnt1,pos+1);
		if(cnt == 100) return ;
	}
}

int main() {
	scanf("%d",&n);
	//把0位置也要算进来 因为是前缀的数量
	ll ans = 1ll * ((n + 1) / 2) * ((n + 2) / 2);
	// cout << ans << '\n';	
	printf("%lld\n",ans);
	dfs(1,0,1);
	return 0;
}

原文地址:https://www.cnblogs.com/forward77/p/15262547.html