递推求解专题训练

想要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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值