【演算法】幾個經典遞推關係記錄
遞推關係數學味很重(貌似很多都是直接有公式的),不過很多記憶搜尋或者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;
}