动态规划-递推

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个盘子里的方法数,可分为两种情况:

  1. i<j 则必有空盘子,空盘子不影响其他苹果的方法,即dp[i][j] = dp[i][j – 1]
  2. 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;
}

    原文作者:B树
    原文地址: https://blog.csdn.net/aonaigayiximasi/article/details/83089257
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞