先说一句题外话 好久没更博客了。。。 希望自己不要退役 rp++
最近在学校学组合数学的时候碰巧做了一道题是这样描述的:
一共有4个格子和
1
,
然后才猛然发现,这题好像在哪见过?(你记得吗 然后写下了这篇博客。。。
【问题描述】
转眼到了2008年的6月9日,盼望已久的高考结束了。我们踏上了向西的旅程(本来是想写西去之路,可是考虑不太妥当)。可可西里,多么诱人的名词,充满了奇幻的色彩和自然的淳朴。从可可西里徒步走回家的决定是在1年半前定下的,而现在,终于可以实现那个钩过手指的预定。我们的可可西里。。。
在回家的路上,疯子和蚊子看到了许多可爱的藏羚羊,无意之中疯子和蚊子发现藏羚羊的居住地的分布也是有规律的,虽然疯子和蚊子早就听说藏羚羊是一种群体性很强又有超高IQ的动物,但是还是为它们的居住地分布规律感到惊叹。经过细心的观察,疯子和蚊子发现,如果假设一个藏羚羊群体有N只羊,就可以把它们的领地当做一个N*N的方阵,在这个方阵上第I列的第I 行都有一个圣地,它们不会居住在圣地,同时每行每列只能居住一只羚羊。于是他们很快算出一个有N只羊的藏羚羊群体的居住地分布方法数。
【问题分析】
相信dalao您也看出来了 题中这道题与上面的那道题是等效的,也就是说圣地的存在就相当于上文的限制条件,所以一起来研究这俩个问题。
那么如何确定 F(n) 呢?
我们就n= 4 的情况来研究(单纯的因为这是样例。。。
那么我们考虑
即
F(n)=?F(n−1)??
那么多出的一行对答案的影响是什么呢?我们不妨假设我们将第
n
行的放置在了位置
Part1
若原来在第
x
列上的放到了第
Part2
若原来在
x
上的没有放到最后面的格子上,那么相当于前
所以递推式是
F(n)=(n−1)∗(F(n−1)+F(n−2))
(
x
共有
需要上高精度,代码写的其丑无比,别打窝……
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int L=3001;
struct bign {
int num[L]; int len;
bign(){memset(num,0,sizeof num);}
bign operator * (const bign &a) const{
bign b;
for (int i=0;i<=len;i++)
for (int j=0;j<=a.len;j++)
b.num[i+j]+=(num[i]*a.num[j]);
b.len=a.len+len;
for (int i=0;i<=b.len;i++)
{
b.num[i+1]+=(b.num[i]/10);
b.num[i]%=10;
}
if (b.num[b.len+1]) b.len++;
return b;
}
bign operator + (const bign &a) const{
bign b; int p=max(len,a.len);
for (int i=0;i<=p;i++)
b.num[i]=num[i]+a.num[i];
b.len=p;
for (int i=0;i<=b.len;i++)
{
b.num[i+1]+=(b.num[i]/10);
b.num[i]%=10;
}
if (b.num[b.len+1]) b.len++;
return b;
}
void print(){
for (int i=len;i>=0;i--)
printf("%d",num[i]);
printf("\n");
return;
}
void pl(){
num[0]++;
for (int i=0;i<=len;i++)
{
if (num[i]>9)
num[i+1]++,num[i]-=10;
else break;
}
if (num[len+1]) len++;
return;
}
}a[2],z;
int n;
int main()
{
scanf("%d",&n);
if (n==1) printf("0\n");
else if (n==2) printf("1\n");
else {
a[0].num[0]=1,a[0].len=0;
a[1].num[0]=2,a[0].len=0;
z.num[0]=3,z.len=0;
int now=1,last=0;
for (int i=4;i<=n;i++)
{
swap(now,last);
a[now]=(a[last]+a[now])*z;
z.pl();
}
a[now].print();
}
return 0;
}