Background
P6174 这篇错排推导非常有趣,然后里面有个 point 是当想要把 (n) 号球放进 (k) 号箱子的时候,分成两种情况讨论:(n) 号箱子里装的是或者不是 (k) 号球。在这里,错排求的方案数是 限制某个箱子不能装某个球,求可行的把球放进箱子,每个箱子里装的是一个的方案数。那么,(n) 号箱子装 (k) 号球,(k) 号箱子装 (n) 号球这个相互放 fact 非常的有意思,通过这个玩法我们将子问题 (D_n) 缩小到了子问题 (D_{n-2})。
令最后盒子 (i) 中装的球为 (mathrm{box}[i]),在原版的问题中显然不合法的条件为存在 (mathrm{box}[i]=i),即箱子 (i) 不能装 (i) 号球。那么如果拓展一下问题,把它反转过来,现在我们求正确的放置方案有多少种。显然,一种:对于 (forall i in [1,n]) 有 (mathrm{box[i]}=i)。那我们放宽一点要求,(mathrm{box}[mathrm{box}[i]]=i),即沿着盒子里球的编号去找另一个盒子里的球找到了,也可以接受,有多少种方案?如果 (mathrm{box}[mathrm{box}[mathrm{box}[i]]]=i) 也可以接受呢?如果 (mathrm{box}[mathrm{box}[mathrm{box}[mathrm{box}[i]]]]) 也可以接受呢?令 (mathrm{box}^mathrm{step}[i]) 表示访问多少层 (mathrm{box}),如上述分别为 (mathrm{step}={1,~2,~3,~4}),如果 (forall i in [1,mathrm{step}]), (mathrm{box}^mathrm{step}[i]) 都可以接受呢?
Solution
这个问题起点在于缩小子问题的这个过程:当然我们考虑局部合法的情况时,首先,显然,把 (k) 球放入箱子 (k) 是一个 (mathrm{step}=1) 的情形;而当我们选择球 (k) 和另一个箱子 (p~(k eq p)) 时,如果 (k) 球放入了 (p),(p) 球放入了 (k),显然就成功构造了一个 (mathrm{step}=2) 的情形。那么可接受的方案有多少种呢?(n=1) 时不存在 (mathrm{step}=2) 而必定 (1) 号球入 (1) 号箱子,一种。(n=2) 时 (1,2) 和 (2,1) 都可以接受,分别是 (mathrm{step}) 为 (1) 和 (2) 的情形,两种。(n=3) 呢?选一个球,然后再选另一个球((n-1) 种选法),这两个球交错插入对方的箱子,此时两个箱子里的球都可以以 (mathrm{step}=2) 被接受,问题变成剩下一个箱子求方案数;又或是这个球插入自己的箱子,这个球以 (mathrm{step}=1) 被接受,求剩下两个箱子的方案数。这样分解问题的一个原因是,当一个大小不超过 (mathrm{step}) 的箱子集合 (S) 里的球的集合 (S'=S) 时,(S) 中的任意一个箱子总能在 (S) 中找到和自己编号一致的球,而 (S) 外的任意一个箱子不会找到 (S) 中的箱子,所以我们可以 drop 掉这几个箱子和球,求解子问题。若设 (mathrm{dp}[i]) 表示剩下 (n) 个球(箱子)时的方案数,即有 (mathrm{dp}[3]=mathrm{dp}[3-2]cdot (3-1)+mathrm{dp}[3-1]=4) 种。此时能发现,我们总能将球数为 (n) 的问题分解为 (n'=n-2) 和 (n'=n-1) 的子问题并合并答案。递推式为:
当 (mathrm{step}=3) 呢?此时,在 (mathrm{step}=2) 的基础之上,我们选择三个箱子 (p, q, r)(((n-1)cdot (n-2)) 个选法),使得 (mathrm{box}[p]=q, mathrm{box}[q]=r, mathrm{box}[r]=p),此时多分解出一个 (n'=n-3) 的子问题
而 (mathrm{step}=4) 则有
对于任意 (1<mathrm{step}le n,mathrm{step}in Z),可能有(未证,猜想有):
Code
写得非常暴力的一个版本,有空再改,包含了对拍和求解
#include <cstdio>
int box[15],ans=0,tot=0;bool used[15];
int n, foo[15], dp[15];
void search(int now){
if(now>n) {
tot++;
for(int i=1;i<=n;i++)
if(!(box[i]==i || box[box[i]]==i || box[box[box[i]]]==i || box[box[box[box[i]]]]==i)) return;
ans++;
for(int i=1;i<=n;i++)
printf("%d ",box[i]);
printf("
");
return;
}
for(int i=1;i<=n;i++)
if(!used[i])
{
box[now]=i;
used[i]=1;
search(now+1);
used[i]=0;
}
}
int main() {
for(int i=1;i<=10;i++) {
ans = 0; tot=0;
n=i;
search(1);
foo[i]=ans;
}
dp[1]=1;dp[2]=2;dp[3]=6;dp[4]=24;
for(int i=5;i<=10;i++) {
dp[i]=dp[i-4]*(i-3)*(i-2)*(i-1)+dp[i-3]*(i-1)*(i-2)+dp[i-2]*(i-1)+dp[i-1];
printf("%d %d %d %s
",i,dp[i],foo[i],dp[i]==foo[i]?"YES":"NO");
}
return 0;
}
Question
(mathrm{step}) 起点可以选择吗?可以中间少几个吗?即,这几个 (mathrm{step}) 不合法的情况下能算吗:
- (2,3,4)
- (1,2,4)
* 最佳答案:我不知道