題意:
給定一個長度為\(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;
}