想要dp入门,感觉自己需要先写几题递推…..
hdu2044 2045 2046 2047 2048 2049 2050
//真正意义上第一题递推
hdu2044
传送门
题目:
有一只经过训练的蜜蜂只能爬向右侧相邻的蜂房,不能反向爬行。请编程计算蜜蜂从蜂房a爬到蜂房b的可能路线数。
其中,蜂房的结构如下所示。
Input
输入数据的第一行是一个整数N,表示测试实例的个数,然后是N 行数据,每行包含两个整数a和b(a < b < 50)
Output
对于每个测试实例,请输出蜜蜂从蜂房a爬到蜂房b的可能路线数,每个实例的输出占一行。
Sample Input
2
1 2
3 6
Sample Output
1
3
思路:假设i为起点,j为终点,那么会发现能直接到达j的点只有两个,j-1和j-2,那么i到j的总数量
d[i][j] = d[i][j-1] + d[i][j-2]; (i < j-2)
d[i][j] = 2; (i==j-2)
d[i][j] = 1; (i==j-1)
那好的,现在递推式就出来了,代码也就出来了。注意数据会爆int,用long long才能存下。
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define inf 0x3f3f3f3f
#define LL long long
LL d[50][50];
int a,b;
int num = 0;
LL dfs(int i,int j)
{
LL ans = -1;
if(d[i][j] != -1) return d[i][j];
if( i+1 == j ) ans = 1;
else if(i+2 == j) ans = 2;
else ans = dfs(i,j-1) + dfs(i,j-2);
return d[i][j] = ans;
}
int main()
{
int t;
for(int i = 0; i < 50; i ++)
for(int j = 0; j < 50; j++)
d[i][j] = -1;
scanf("%d",&t);
for(int i = 1; i <= 50; i++)
for(int j = i+1; j <= 50; j++)
dfs(i,j);
while(t--)
{
num = 0;
scanf("%d%d",&a,&b);
cout<<d[a][b]<<endl;
}
return 0;
}
//hdu2045
题目:
人称“AC女之杀手”的超级偶像LELE最近忽然玩起了深沉,这可急坏了众多“Cole”(LELE的粉丝,即”可乐”),经过多方打探,某资深Cole终于知道了原因,原来,LELE最近研究起了著名的RPG难题:
有排成一行的n个方格,用红(Red)、粉(Pink)、绿(Green)三色涂每个格子,每格涂一色,要求任何相邻的方格不能同色,且首尾两格也不同色.求全部的满足要求的涂法.
以上就是著名的RPG难题.
如果你是Cole,我想你一定会想尽办法帮助LELE解决这个问题的;如果不是,看在众多漂亮的痛不欲生的Cole女的面子上,你也不会袖手旁观吧?
Input
输入数据包含多个测试实例,每个测试实例占一行,由一个整数N组成,(0 < n<= 50)。
Output
对于每个测试实例,请输出全部的满足要求的涂法,每个实例的输出占一行。
Sample Input
1
2
Sample Output
3
6
思路:
这题我没找到递推式,但是我打了个表找了个规律…….用dfs暴搜打了个表,发现要么2*f[i-1]+6,
2*[f-1]-6…..
其实仔细想一想就可以得出:当直到前n-1项的 不同的排列数,这个时候如果第n-1项和第一项相同,那么第n项就有两种可能,如果不同,就只有一种可能,也即是:
f[i] = f[i-1] + 2*f[i-2];
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define LL long long
LL s[55];
int main()
{
s[1] = 3; s[2] = 6; s[3] = 6;
for(int i = 4; i < 55; i++)
{
if(i%2 == 0)
s[i] = s[i-1]*2+6;
else s[i] = s[i-1]*2-6;
}
int n;
while(~scanf("%d",&n))
{
printf("%I64d\n",s[n]);
}
return 0;
}
//hdu2046
题目:
在2×n的一个长方形方格中,用一个1× 2的骨牌铺满方格,输入n ,输出铺放方案的总数.
例如n=3时,为2× 3方格,骨牌的铺放方案有三种,如下图:
Input
输入数据由多行组成,每行包含一个整数n,表示该测试实例的长方形方格的规格是2×n (0 < n <= 50)。
Output
对于每个测试实例,请输出铺放方案的总数,每个实例的输出占一行。
Sample Input
1
3
2
Sample Output
1
3
2
思路:假设知道前n-1项的不同的排列和,那么第n个,我们会发现,只有两种情况,一种是前n-2项的排列数,然后横着堆俩块,另一种是前n-1项的,竖着堆一块。
a[i] = a[i-1] + a[i-2];
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define LL long long
LL a[55];
int main()
{
a[1] = 1; a[2] = 2;
for(int i = 3; i <= 50; i++)
a[i] = a[i-1] + a[i-2];
int n;
while(~scanf("%d",&n))
{
printf("%I64d\n",a[n]);
}
return 0;
}
hdu2047
题目大意: 由EOF三种字符组成长度为n的字符串,要求两个O不能相邻,求共有多少种方法。
思路:想的方法很奇怪,我把有O为结尾的分为了一组,没有O结尾的分为了一组,这样就好想了。
其实不用三个数组的话,是可以这样的,知道前n-1的ans值,固定第n项为O时,n
-1项不能为O,就有2*ans[i-2],如果n项不是O,那么就是2*ans[i-1];,即
ans[i] = 2*ans[i-2] + 2*ans[i-1];
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define LL long long
LL have0[50];
LL nothave[50];
LL ans[50];
int main()
{
have0[1] = 1; have0[2] = 2;
nothave[1] = 2; nothave[2] = 6;
ans[1] = 3; ans[2] = 8;
for(int i = 3; i < 50; i++)
{
ans[i] = have0[i-1] * 2 + nothave[i-1] * 3;
have0[i] = nothave[i-1];
nothave[i] = ans[i] - have0[i];
}
int n;
while(~scanf("%d",&n))
{
printf("%I64d\n",ans[n]);
}
return 0;
}
hdu2048
题目大意: n个数字的错排问题。
思路: 知道d[1] 到 d[n-1], 此时的第n个人有两种情况,一种是拿到了自己的号,这时与前面任意一人换即可,即(n-1)*d[n-1], 若不是拿的自己的,则前面必有一个人拿的第n号,而剩下的n-2人必须满足错排列,故有(n-1)*d[n-2];
d[n] = (n-1)*(d[n-1] + d[n-2]);
#include <cstdio>
#include <iostream>
using namespace std;
#define LL long long
LL fac[20];
LL f[20];
int main()
{
fac[1] = 1;
for(int i = 2;i <= 20; i++)
{
fac[i] = i*fac[i-1];
//cout<<fac[i]<<endl;
}
f[1] = 0; f[2] = 1; f[3] = 2;
for(int i = 4; i <= 20; i++)
f[i] = (i-1)*(f[i-1]+ f[i-2]);
int n,t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
cout<<f[n]<<' '<<fac[n]<<endl;
double ans = (double)f[n]*100/fac[n];
printf("%.2f%%\n",ans);
}
return 0;
}
hdu2049
题目大意: n个数里m个数错排
思路: 上一题已经知道错排怎么求了,所以这一题就是Cmn * d[m];这题数组还开小了一个…简直智障…
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define LL long long
LL f[25];
LL fac[25];
int main()
{
fac[0] = 1;
fac[1] = 1; f[1] = 0; f[2] = 1; f[3] = 2;
for(int i = 2; i <= 20; i++)
{
fac[i] = i*fac[i-1];
// cout<<fac[i]<<endl;
}
for(int i = 4; i <= 20; i++)
{
f[i] = (i-1)*(f[i-1]+f[i-2]);
}
int t;
scanf("%d",&t);
while(t--)
{
int n,m;
scanf("%d%d",&n,&m);
LL x = fac[n]/fac[n-m]/fac[m];
// cout<<x<<endl;
cout<<x*f[m]<<endl;
}
return 0;
}
hdu2050
题目: 问n条折线能将平面分为多少份
我空间想象力差的不是一点半点。。。
代码:
#include <cstdio>
#include <cstring>
using namespace std;
#define LL long long
LL data[10005];
int main()
{
data[1] = 2;
for(int i = 2; i < 10005; i++)
data[i] = data[i-1] + 4*(i-1) + 1;
int t,n;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
printf("%I64d\n",data[n]);
}
return 0;
}