intcount(vector<int>& src, const vector<int>& target){ auto change = [&](int x) -> int { int s = 0, t = 0; for (int i = 0; i < n; i++) { if (src[i] == x) s = i; if (target[i] == x) t = i; } int cnt = 0; if (s > t) for (int i = s; i > t; i--) swap(src[i], src[i-1]), cnt++; elsefor (int i = s; i < t; i++) swap(src[i], src[i+1]), cnt++; return cnt; };
int res = 0; for (int i = 0; i < n; i++) { res += change(target[i]); } return res; }
intmain(){ freopen("table.out", "w", stdout); vector<int> p(n), src; for (int i = 0; i < n; i++) p[i] = i+1; bool first = true; do { if (!first) cout << count(src, p) << '\n'; src = p; first = false; } while (next_permutation(p.begin(), p.end())); return0; }
通过更改 n 的值造成的影响可以列出来,这里循环节指的是包含最大数字的循环节:
n
最大数字
循环节长度
总长
2
1
1
1
3
2
2
5
4
4
6
23
5
7
24
119
6
11
120
719
7
16
720
5039
可以人为的把总长加一,然后明显观察到这是阶乘的关系,然后最大数字的差分是 1,2,3,4,5 递增的关系,因此我们进行求和,可以认为对于长度为 n 的排列,一开始总长为 n! 的数列中全是 1:
隔 2! 数字加 1
隔 3! 数字加 2
隔 4! 数字加 3
隔 5! 数字加 4
因此:
f(n)=n!+2!n!×1+3!n!×2+⋯=n!+n!i=2∑ni!i−1
我们最后把最大的那个数减掉,是 1+2n(n−1)。
现在我们可以计算出长度为 n 的排列从 1∼n 到 n∼1 需要的操作次数了,考虑按照康托展开那样计算。
考虑到我们上面是按照 n 个循环节来计算的,所以我们这里有长度为 n−i+1 的 cnt(x) 个循环节,猜想直接累加 cnt(x)×f(n−i+1)/(n−i+1),发现答案正确。