递推关系数学味很重(貌似很多都是直接有公式的),不过很多记忆搜索或者dp的状态转移都是要自己推理的,记录几个俗爆炸的递推关系...
文科生学计算机......我呵呵一脸 :(
Fibonacci
这个很常见,第一次刷题愣是没找出来...觉得特难(文科生的悲哀)!
目前见过的有 : 兔子生兔子(竟然全是母的还都有孕)母牛生小牛 ; 超级楼梯(一次爬一阶或者两阶) ; 蜜蜂的巢等
总之模型大概是, 现有的 = 已有的+新生的(不一定是f(n-1)和f(n-2))
#include<cstdio>
long long a[50];
int main()
{
int n;
a[1]=a[2]=1;
scanf("%d",&n);
for(int i=3;i<=n;i++)
a[i]=a[i-1]+a[i-2];
for(int i=1;i<=n;i++)
printf("%lld ",a[i]);
return 0;
}
Catalan数
最经典的应该是划分凸多边形吧: 固定点n和点1 , 利用k的位置进行分类(2<=k<=n-1),左边是凸k边形 右边是凸n-k+1边形
顺推求解(有点记忆化的味道)
#include<cstdio>
#include<cmath>
using namespace std;
long long a[50];
int main()
{
int n;
a[2]=a[3]=1;
scanf("%d",&n);
for(int i=4;i<=n;i++)
for(int k=2;k<=i-1;k++)
a[i]+=a[i-k+1]*a[k];
for(int i=3;i<=n;i++)
printf("%lld ",a[i]);
return 0;
}
危险的组合
这个太有名了...
对于n阶段,从首推到尾部: 假设当前的i是末尾,使得有三个铀连在一起的情况分两种:
1. 前i-1个已经满足条件,此时数量是a[i-1]*2
2. 前i-2和i-1是铀,第i-3个不是,此时数量是pow(2,i-4)-a[i-4];
对于第二种情况是有点恶心的,不过这么想:i-4以前的再怎么摆跟i也没什么卵关系,然后a[i-4]代表i-4做尾部,合法的摆法
#include<cstdio>
#include<cmath>
using namespace std;
long long a[50];
int main()
{
int n;
a[3]=1;a[4]=3;
for(int i=4;i<=20;i++)
a[i]=2*a[i-1]+(pow(2,i-4)-a[i-4]);
while(scanf("%d",&n)!=-1)
printf("%lld\n",a[n]);
return 0;
}
这个第一次见是装信问题:求n封信全错的装填方案数
随便挑出两份A,B,设第i份为A且装错:
1. AB恰好对调,那么这样的B有i-1个
2. A错了,但B不是A的信,那么我们去掉A,设A的信装在B里是对的(因为B已经没对应的信了),恰好划分出一个子问题...(好巧~),这样数量有a[i-2]*(i-1)份(这样的B有i-1个)
最后加法原理...
#include<cstdio>
long long dp[20];
int main(){
int n;
dp[2]=1;dp[3]=2;
for(int i=3;i<=20;i++)
dp[i]=(dp[i-1]+dp[i-2])*(i-1);
scanf("%d",&n);
printf("%lld\n",dp[n]);
return 0;
}
话说有n个格子,对这些格子涂色Red Pink Green ,要求相邻颜色不同,首尾颜色不同,求涂色方案数....
笔者第一刷就考虑奇偶去了....(文科生!)
还是从首到尾枚举i为末尾的情况:
1. 若i-1和第一个同色: a[i]=2*a[i-2];(因为对于i-2来说,i-1可以算做第一个,然后i可以涂两个色)
2. 若i-1和第一个异色:第i个只能涂一个颜色,所以a[i]=a[i-1]
最后分类加法....
#include<cstdio>
using namespace std;
long long dp[55];
int main(){
int n;
dp[1]=3;dp[2]=6;dp[3]=6;
for(int i=4;i<=50;i++)dp[i]=2*dp[i-2]+dp[i-1];
while(scanf("%d",&n)!=-1){
printf("%lld\n",dp[n]);
}
return 0;
}
其实前面都水,是基础,不过这个倒是有点意思...
和全错位不同的是这里要求n份信,放错的没超过一半的方案总数;(也就是对的在一半及以上...)
组合数学:乘法原理
枚举装对的从一半到全部:
设当前装对K封,从n取k有C(n,k)种(文科生注意了,这是组合公式...),剩下的那些全错的可能数有a[n-i]种(a是上面算出来的全错位.....),再乘法原理
最后对枚举的那些用加法原理...(组合数学!文科生!)
#include<cstdio>
long long c[55],cnt;
int n;
long long C(int n,int m){
double ans=1;
for(int i=0;i<m;i++)
ans*=(1.0*n-i)/(i+1.0);
return (long long)ans;
}
int main(){
int m,i;
c[2]=1;
for(int i=3;i<=25;i++)c[i]=(i-1)*(c[i-1]+c[i-2]);
while(scanf("%d",&n)!=-1&&n){
m=n/2+n%2;
cnt=0;
for(i=m;i<=n;i++)
cnt+=C(n,i)*c[n-i];
printf("%lld\n",cnt+1);
}
return 0;
}