hdu2044
http://acm.hdu.edu.cn/showproblem.php?pid=2044
1到n的路径数f[n]有两种来源,f[n-1]的路径,f[n-2]的路径
编号a到b的蜂房可以看作编号1到b-a的蜂房
#include <iostream>
using namespace std;
long long f[55];
int n;
int main()
{
f[1] = 1;
f[2] = 2;
for(int i = 3; i <= 55; i++)
{
f[i] = f[i - 1] + f[i - 2];
}
int a, b;
cin >> n;
while(n--)
{
cin >> a >> b;
cout << f[b - a] << endl;
}
return 0;
}
hdu2045
http://acm.hdu.edu.cn/showproblem.php?pid=2045
f[n]为n长度有多少种涂法,此时第n个位置与第1个位置颜色已经不同,来看第n-1个位置,若也与第1个位置不同,即f[n-1]种;若与第1个位置相同,则第n-2个位置肯定与第1个位置不同,可以涂其他两种颜色,即f[n-2]*2
#include <iostream>
using namespace std;
long long f[55];
int main()
{
f[1] = 3;
f[2] = 6;
f[3] = 6;
for(int i = 4; i <= 55; i++)
{
f[i] = f[i - 1] + 2 * f[i - 2];
}
int n;
while(cin >> n)
{
cout << f[n] << endl;
}
return 0;
}
hdu2046
http://acm.hdu.edu.cn/showproblem.php?pid=2046
用f[i]表示2Xi的方格铺满骨牌的方案数,那么考虑第i列,要么竖着放置一个骨牌;要么连同i-1列,横着放置两个骨牌,如图2所示。由于骨牌的长度为1X2,所以在第i列放置的骨牌无法影响到第i-2列。很显然,两块黑色的部分分别表示f[i-1]和f[i-2],所以可以得到递推式f[i] = f[i-1] + f[i-2] (i >= 2),并且边界条件f[0] = f[1] = 1。
#include <iostream>
using namespace std;
long long f[60];
int n;
int main()
{
f[1] = 1;
f[2] = 2;
for(int i = 3; i <= 60; i++)
f[i] = f[i - 1] + f[i - 2];
while(cin >> n)
{
cout << f[n] << endl;
}
return 0;
}
hdu1143
http://acm.hdu.edu.cn/showproblem.php?pid=1143
首先可以明确当N等于奇数的时候,方案数一定为0。所以如果用f[i] (i 为偶数) 表示3Xi的方格铺满骨牌的方案数,f[i]的方案数不可能由f[i-1]递推而来。那么我们猜想f[i]和f[i-2]一定是有关系的,如图一 -1-3所示,我们把第i列和第i-1列用1X2的骨牌填满后,轻易转化成了f[i-2]的问题,那是不是代表f[i] = 3*f[i-2]呢?
仔细想想才发现不对,原因是我们少考虑了情况,这些情况用上图情况无法表示,再填充完黑色区域后,发现和f[i-4]也有关系,但是还是漏掉了一些情况。
当一维的状态已经无法满足我们的需求时,我们可以试着增加一维,用二维来表示状态,用f[i][j]表示(3 X i) + j个多余块的摆放方案数
转化成二维后,我们可以轻易写出三种情况的递推式。
f[i][0] = f[i-2][0] + f[i-1][1] + f[i-2][2]
f[i][1] = f[i-1][2]
f[i][2] = f[i][0] + f[i-1][1]
边界条件 f[0][0] = f[1][1] = f[0][2] = 1
#include <iostream>
using namespace std;
int dp[30][3];
int main()
{
int n;
dp[0][0] = dp[1][1] = dp[0][2] = 1;
for(int i = 2; i <= 30; i++)
{
dp[i][0] = dp[i-2][0] + dp[i-1][1] + dp[i-2][2];
dp[i][1] = dp[i-1][2];
dp[i][2] = dp[i][0] + dp[i-1][1];
}
while(cin >> n && n != -1)
{
cout << dp[n][0] << endl;
}
return 0;
}
hdu2047
http://acm.hdu.edu.cn/showproblem.php?pid=2047
f[n]表示n长度序列的涂法,若位置n为E或F,则n-1序列涂法为f[n-1],共f[n-1]*2种;若位置n为O,则位置n-1肯定为E或F,则n-2序列涂法为f[n-2],共f[n-2]*2种。
#include <iostream>
using namespace std;
long long f[45];
int main()
{
f[1] = 3;
f[2] = 8;
for(int i = 3; i <= 45; i++)
{
f[i] = 2 * (f[i -1] + f[i - 2]);
}
int n;
while(cin >> n)
{
cout << f[n] << endl;
}
return 0;
}
hdu2048
http://acm.hdu.edu.cn/showproblem.php?pid=2048
全错排列
记f[n]为数量n的全错排列数
假设正确排列是A对a,B对b…
若A和b匹配了,B也和a匹配了,则剩下的错排数为f[n-2];
若A和b匹配了,B没和a匹配,错排数位f[n-1];
因为A还可以与其他n-1个匹配,f[n] = (n-1) * (f[n-1] + f[n-2])
#include <iostream>
#include <stdio.h>
using namespace std;
long long f[20];
int main()
{
int c, n;
long long sum;
f[1] = 0;
f[2] = 1;
for(int i = 3; i <= 20; i++)
f[i] = (i - 1) * (f[i - 1] + f[i - 2]);
scanf("%d", &c);
while (c--)
{
sum = 1;
scanf("%d", &n);
for(int i = 1; i <= n; i++)
sum *= i;
printf("%.2lf%%\n", 1.0 * f[n] / sum * 100);
}
return 0;
}
hdu2049
http://acm.hdu.edu.cn/showproblem.php?pid=2049
先从n个选出m个,再错排
#include <iostream>
#include <stdio.h>
using namespace std;
long long f[20];//错排
long long b[20];//阶乘,求C(n, m)
int main()
{
int T;
int n, m;
long long sum;
f[1] = 0;
f[2] = 1;
for(int i = 3; i <= 20; i++)
f[i] = (i - 1) * (f[i - 1] + f[i - 2]);
b[0] = 1;
b[1] = 1;
b[2] = 2;
for(int i = 3; i <= 20; i++)
{
b[i] = b[i - 1] * i;
}
scanf("%d", &T);
while (T--)
{
cin >> n >> m;
cout << b[n] / b[m] / b[n - m] * f[m] << endl;
}
return 0;
}
hdu2050
http://acm.hdu.edu.cn/showproblem.php?pid=2050
先分析下直线分割平面的情况,增加第n条直线的时候,跟之前的直线最多有n-1个交点,此时分出的部分多出了(n-1)+1;
折线也是同理,f(1)=2,f(2)=7,先画好前面n-1条折线,当增加第n条折线时,此时与图形新的交点最多有2 * 2 * (n-1)个,所以分出的部分多出了2 * 2 * (n-1)+1个面,所以推出f(n)=f(n-1)+4 * (n-1)+1,n>=3
#include <iostream>
using namespace std;
int main()
{
int T;
int n;
long long f[10000];
f[1] = 2;
f[2] = 7;
for(int i = 3; i <= 10000; i++)
{
f[i] = f[i - 1] + 4 * (i - 1) + 1;
}
cin >> T;
while(T--)
{
cin >> n;
cout << f[n] << endl;
}
return 0;
}
poj1664
http://poj.org/problem?id=1664
m个苹果放n个盘子里,若用dp[i][j]来表示i个苹果放到j个盘子里的方法数,可分为两种情况:
- i<j 则必有空盘子,空盘子不影响其他苹果的方法,即dp[i][j] = dp[i][j - 1]
- i>j 苹果多,但题目允许有空盘子,则此时又分为有无空盘子,有时dp[i][j] = dp[i][j - 1],没有时可以从每个盘子中拿出一个苹果,即变为向 i - j 个盘子中放 j 个苹果,直到有空盘子为止,dp[i][j] = dp[i - j][j],总方法数就是两者相加
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int MAXN = 15;
int dp[MAXN][MAXN];
int m, n;
int main()
{
int t;
cin >> t;
while(t--)
{
memset(dp, -1, sizeof(dp));
cin >> m >> n;
for(int i = 1; i <= 10; i++)
dp[0][i] = dp[1][i] = dp[i][1] = 1;
for(int i = 1; i <= 10; i++)
{
for(int j = 1; j <= 10; j++)
{
if(dp[i][j] == -1)
{
if(i < j)
dp[i][j] = dp[i][j-1];
else
dp[i][j] = dp[i][j-1] + dp[i-j][j];
}
}
}
cout << dp[m][n] << endl;
}
return 0;
}