递推求解专题训练

来源:互联网 发布:labview编程详解书 编辑:程序博客网 时间:2024/05/26 07:30

想要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 longLL 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 longLL 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 longLL 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 longLL 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 longLL 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 longLL 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 longLL 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;}
0 0
原创粉丝点击