2023年11月第一周第二次总结

1. 动态规划

在我看来动态规划就是用一种缓存机制来保存之前求解的答案,如果要再次用到已经求解过的答案就直接把缓存里面的答案给他而不必再次求解,也就是用空间换取时间

那么要解决动态规划问题,最好按照以下步骤来求解

      1. 用暴力递归来求解问题
      2. 能用记忆化搜索就先用记忆化搜索来优化递归,时间复杂度是O(N)
      3. 用状态转移方程来解决问题

解题思路

字有点丑,见谅。

解题方法

暴力递归

//纯暴力递归 
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#define N 51//数组的最大长度 
long long count;//用来计数方案 
void f(int n);//n代表长方形长度 

int main(int argc, char* argv[])
{
    int n;//输入长方形的长度 
    long long res[N];//存放结果 
    int m = 0;//记录结果的个数 

    while(~scanf("%d", &n));
    {
        count = 0;//每次初始化计数为0 
        f(n);//递归求方案数 
        res[m++] = count;//把求得的结果放在结果数组保存起来 
    }

    //打印结果 
    for (int i = 0; i < m; i++)
    {
        printf("%lld\n", res[i]);
    }

    return 0;
}
void f(int n)
{
    if (n == 0)//如果长方形的长度为0,代表已经把长方形铺满了,方案数加1 
    {
        count++;
        return;//结束递归,回到上一次递归调用 
    }

    if (n >= 3)//如果长度大于等于3,就可以铺三种骨牌,即铺长度为1*3的骨牌、1*2的骨牌、1*1的骨牌 
    {
        f(n - 3);//铺1*3的骨牌,所以长度减3 
        f(n - 2);//铺1*2的骨牌,所以长度减2 
        f(n - 1);//铺1*1的骨牌,所以长度减1 
    } 
    else if (n == 1){//如果长度为1,就只能铺长度为1*1的骨牌 
        f(n - 1);
    }
    else {//如果长度为2,那么就可以铺1*2的骨牌和1*1的骨牌 
        f(n - 2);//铺1*2的骨牌,所以长度减2
        f(n - 1) ;//铺1*1的骨牌,所以长度减1 
    }
    return ;//可以省略 
}

记忆化搜索

//记忆化搜索 
#include <stdio.h>
#include <string.h>
#define N 100

long long dp[N];//用来记录已经算出来的答案 

long long f(int n)//n代表长方形的长度 
{
    if (dp[n] != -1)  {//表示答案已经在我的缓存表里直接返回结果 
    	return dp[n];
	} 
	
	long long int count = 0;//用来统计有多少个方案数 
	
	if (n == 0) {//如果长方形的长度为0,代表已经把长方形铺满了,返回1 
		return 1;
	}
	
	if (n >= 3)  {//如果长度大于等于3,就可以铺三种骨牌,即铺长度为1*3的骨牌、1*2的骨牌、1*1的骨牌 
		count += f(n - 3);//加上铺1*3的骨牌的方案数 
		count += f(n - 2);//加上铺1*2的骨牌的方案数
		count += f(n - 1);//加上铺1*1的骨牌的方案数
	}
	else if (n >= 2) {//如果长度为2,那么就可以铺1*2的骨牌和1*1的骨牌
		count += f(n - 2);//加上铺1*2的骨牌的方案数
		count += f(n - 1);//加上铺1*1的骨牌的方案数
	}
	else {//如果长度为1,就只能铺长度为1*1的骨牌
		count += f(n - 1);//加上铺1*1的骨牌的方案数
	}
	
	dp[n] = count;//把求出来的答案保存到我的缓存表里面 
	
	return count;//返回方案数 
}

int main(int argc, char* argv[])
{
    int n;//表示长方形的长度 
    long long res[N];//存放结果 
    int m = 0;//记录结果的个数 
    
    //注意这个函数要在c++才能使用 
    memset(dp, -1, sizeof(dp));//初始化dp数组让每个元素初始化为-1,-1表示答案没有求出来
    
    while (scanf("%d", &n) == 1)
    {
        res[m++] = f(n);//把结果保存起来 
    }

	//打印结果 
    for (int i = 0; i < m; i++)
    {
        printf("%lld\n", res[i]);
    }
    
    return 0;
}

动态规划

//动态规划 
#include <stdio.h>
#include <string.h>
#define N 100

long long dp[N];//dp数组用来记录之前的结果 

void solve()
{
    //通过暴力递归的出来的
    dp[0] = 1;
    dp[1] = 1;
    dp[2] = 2;

	/*
	对于每个 n,铺法数量等于前三个位置的铺法数量之和。这是由于对于 n 个位置的地板,我们可以在最后一个位置放置一块 1 x 1 的砖块,或者在最后两个位置放置一块 2 x 1 的砖块,或者在最后三个位置放置一块 3 x 1 的砖块。因此,铺法数量等于这三种情况下的铺法数量之和
	*/

    for (int i = 3; i < N; i++)//数组的下标表示 n 的取值,dp[i] 表示当 n = i 时的铺法数量。
    {
        dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3];
    }
}

int main()
{
    int n;
    long long res[N];//存放结果 
    int m = 0;//记录结果的个数 

    memset(dp, 0, sizeof(dp));
    solve();//求出所有的结果 

    while (scanf("%d", &n) == 1)
    {
        res[m++] = dp[n];//把结果存起来 
    }

	//打印结果 
    for (int i = 0; i < m; i++)
    {
        printf("%lld\n", res[i]);
    }

    return 0;
}

总结

对于动态规划的题目,建议先尝试写出来它暴力递归的解法,在来尝试找到状态转移方程,来求解出动态规划的题目。

posted @ 2023-11-05 19:02  lwj1239  阅读(45)  评论(0)    收藏  举报