School Team Contest 2 (Winter Computer School 2010/11) - I. Toys(受限增长字符串)
原题链接:I. Toys
题目大意
玛莎将 nnn 个玩具(编号隐含为 1∼n1\sim n1∼n)分成若干堆,哥哥萨沙却把所有玩具合并成了一堆。为哄玛莎开心,萨沙需要尝试所有可能的玩具分堆方式,直到玛莎认可;且每次调整只能进行 单次操作(从任意一堆取一个玩具,移到另一堆,或单独成为一个新堆),要求每个分堆方式仅尝试一次,初始状态为所有玩具在同一堆。
样例输入:
第一行输入为测试样例个数
3
样例输出:
5
{1,2,3}
{1,2},{3}
{1},{2,3}
{1},{2},{3}
{1,3},{2}
题目分析
对于一个集合的分区,可以用一个字符串来编码,这个字符串被称为受限增长字符串。
- 受限增长字符串:对于字符串 a1,a2,…,ana_1,a_2,\dots,a_na1,a2,…,an,它满足 a1=0a_1=0a1=0 且 aj+1≤1+max(a1,…,aj)a_{j+1}\le1+\max(a_1,\dots,a_j)aj+1≤1+max(a1,…,aj)。当且仅当元素 iii 和 jjj 在分区中属于同一个子集时,它们在字符串中对应的字符相等。例如,分区 {1,3},{2},{4}\{1,3\},\{2\},\{4\}{1,3},{2},{4} 的字符串表示为 010201020102。
吉迪恩 - 埃利希发明了一种简便方法来建立受限增长字符串列表,以下简称 埃利希方案。
假设已经有了长度为 n−1n-1n−1 的受限增长字符串列表 s1,s2,…,sks_1,s_2,\dots,s_ks1,s2,…,sk,需要从中得到长度为 nnn 的列表。对于 si=a1a2…an−1s_i=a_1a_2\dots a_{n-1}si=a1a2…an−1,令 m=1+max(a1,…,an−1)m=1+max(a_1,\dots,a_{n-1})m=1+max(a1,…,an−1)。如果 iii 为奇数,将数字 0,m,m−1,…,1,0,m,m-1,\dots,1,0,m,m−1,…,1, 依次追加到 sis_isi 后面,得到长度为 nnn 的字符串;如果 iii 为偶数,则按照 1,…,m−1,m,01,\dots,m-1,m,01,…,m−1,m,0 的顺序追加数组。该算法的时间复杂度为 O(n⋅B(n))O(n\cdot B(n))O(n⋅B(n)),其中 B(n)B(n)B(n) 为贝尔数,是超指数级的增长速度,当 n=10n=10n=10 时约为 10510^5105。
代码答案
#include<bits/stdc++.h>
#define endl '\n'
#define pb push_back
#define all(x) x.begin(), x.end()using namespace std;
using vi = vector<int>;
using vvi = vector<vector<int>>;
using vstr = vector<string>;// 埃利希方案生成所有长度为 n 的受限增长字符串
vstr Init(int n) {if(n == 1) return {"0"};auto pre = Init(n - 1);vstr res;for(size_t i = 0; i < pre.size(); i++) {auto &s = pre[i];int m = *max_element(all(s)) - '0' + 1;vi app = {0};for(int k = m; k >= 1; k--) app.pb(k);if(i & 1) {for(int j = m; j >= 0; j--) res.pb(s + char('0' + app[j]));} else {for(int j = 0; j <= m; j++) res.pb(s + char('0' + app[j]));}}return res;
}int main() {ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);int n; cin >> n;vstr s = Init(n);cout << s.size() << endl;for(size_t i = 0; i < s.size(); i++) {vvi q;for(int j = 1; j <= n; j++) {int pos = s[i][j - 1] - '0';if(q.size() == pos) q.pb({j});else q[pos].pb(j);}for(size_t j = 0; j < q.size(); j++) {cout << '{';for(size_t k = 0; k < q[j].size(); k++) cout << q[j][k] << ",}"[k == q[j].size() - 1];cout << ",\n"[j == q.size() - 1];}}return 0;
}