蓝桥杯算法入门_10 (动态规划)

目录

走楼梯 (斐波那契)

杨辉三角 (dp - 二维数组)

过河卒

走马

回家最短路径

已知状态转移方程 ,求最大值

回家问题的递推式

动态规划入门题

爬楼梯升级:每次可以跳跃任意奇数的台阶

弹小球

传娃娃

逃生-生命值

一维消消乐

墙壁涂色


走楼梯 (斐波那契)


(一次走1或2阶) f(n) = f(n - 1) + f(n - 2);
兔子生兔子 ---斐波那契
直线分割立体图形  , f(n) = f(n - 1) + n;

输入:
5

输出:
8
8

#include <iostream>
using namespace std;
const int N_01 = 1e3;  //规范性写法
typedef long long ll;  //简写
ll f_01[N_01];

void test_01() {
    int n;
    cin >> n;
    f_01[0] = f_01[1] = 1;
    for(int i = 2; i <= n; i++) {     //写法一
        f_01[i] = f_01[i - 1] + f_01[i - 2];
    }
    cout<< f_01[n] << endl;
    ll a = 1, b = 1, c = 1;
    for(int i = 2; i <= n; i++) { //写法二
        c = a + b;
        a = b;
        b = c;
    }
    cout << c <<endl;
    return;
}


杨辉三角 (dp - 二维数组)


杨辉三角递推式: (多项式高次展开 -- 系数)
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
(对角线、第一列为1)

f[i][j]  =  f[i - 1][j - 1] + f[i - 1][j]  //上面一层的元素加和 等于下面一层



const int N_triangle = 55;
long long f_triangle[N_triangle][N_triangle];

void init() {
    for(int i = 1; i <=N_triangle; i++) { //这样是从 1 , 1开始
        for(int j = 1; j <= i ; j++) {
            if(i == 1) {
                f_triangle[i][j] = 1;
            } else {
                f_triangle[i][j]  =  f_triangle[i - 1][j - 1] + f_triangle[i - 1][j];
            }
        }
    }
    return;
}

得到对应位上的值

输入:
3 3

输出:
1


void test_init() {
    init();
    int n,m;
    cin>> n >> m;
    cout<< f_triangle[n][m]<<endl;  //查看 坐标值

    for(int i = 0; i <= n; i++) { //i <= n ,j最后为一次为 n
        for(int j = 1; j <= i ; j++) {   //i最后为执行的为 n - 1带入j停止
            cout<< f_triangle[i][j] << " ";
        }
        cout<<endl;
    }
}

void Yang_hui_triangle() {  //my_AC
    int n;
    cin >> n;
    for(int i = 0; i < n; i++) {
        f[i][0] = 1;
        f[i][i] = 1;
    }

    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) {
            f[i][j]  =  f[i - 1][j - 1] + f[i - 1][j];
        }

    }
    for(int i = 0; i < n; i++) {
        for(int j = 0; j <= i; j++) {   //i最后为执行的为 n - 1带入j停止
            cout<< f[i][j] << " ";
        }
        cout<<endl;
    }

    return;
}

过河卒


分析: 如果直接搜索从A到B的所有路径,计算速度很慢

用递推式求出起点到终点的路径数目
F(i,j) = F(i - 1 , j) + F(i , j - 1)

边界条件 (0,0)   ,  F(0,0) = 1;  //控制点设为0


const int N_02 = 1000;
long long f_02[N_02][N_02];

void test_02() {
    int n = 4,m = 8;
    for(int i = 0; i < n; i++) {
        for(int j = 0; j < m; j++) {
            if(i != 0) {
                f_02[i][j] = f_02[i][j] + f_02[i - 1][j];
            }
            if(j != 0) {
                f_02[i][j] = f_02[i][j] + f_02[i][j - 1];
            }
        }
    }
    return;
}

走马


dp法(动态规划 -- 递推式)
有多少种方案

输入n,m,cx,cy
n,m 棋盘行列大小  ,cx,xy 马的坐标位置

输入:
5 5 2 4

输出:
14


int dir_03[8][2] = { {1,2},{2,1},{-1,2},{-2,1},{-2,-1},{-1,-2},{1,-2},{2,-1} };
bool d_03[30][30]; //标记状态
long long dp_03[30][30];  //long long  递推式
void test_03() {
    int n,m,cx,cy;  //n,m 棋盘行列大小  ,cx,xy 马的坐标位置
    cin >> n >> m >> cx >> cy ;
    d_03[cx][cy] = true;//设为起始true可走
    for(int i = 0; i < 8; i++) {
        int tx = cx + dir_03[i][0];
        int ty = cy + dir_03[i][1];
        if(tx >= 0 && tx <= n && ty >= 0 && ty <=m ) {
            d_03[tx][ty] = true;
        }
    }
    dp_03[0][0] = 1;//起始值 ,临界值 !!
    for(int i = 0; i <= n; i++) { //遍历棋盘 --动态规划--递推式
        for(int j = 0; j <= m; j++) {
            if(d_03[i][j] == false) {
                if(i) { //i != 0  才能从上走
                    dp_03[i][j] +=  dp_03[i - 1][j];
                }
                if(j) { // j != 0 才能从左走
                    dp_03[i][j] +=  dp_03[i][j - 1];
                }
            }
        }
    }
    cout << dp_03[n][m] <<endl;  //有多少种方案
    return;
}


动态规划算法常用于某种最优解  ,求出多种可行性 ,找出最优解

状态

决策

状态转移方程

需最优化原理  :前一个策略状态必须最优
最优策略的子策略也是最优的


回家最短路径


输入: 地点两两之间距离(x,y坐标系) ,几个中转点
3
0 3 4
6 2 5
5 4 3

输出:
12


#include<algorithm>
int a_04[110][110];
int dp_04[110][110];

void test_04() {
    int n;
    cin >> n;
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) {
            cin >> a_04[i][j];
        }
    }
    dp_04[1][1] = 0;
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) {
            if(i == 1 && j == 1) {
                continue;
            } else if(i == 1) {
                dp_04[i][j] =     dp_04[i][j - 1] +     a_04[i][j];
            } else if(j == 1) {
                dp_04[i][j] =     dp_04[i - 1][j] +     a_04[i][j];
            } else {
                dp_04[i][j] =     min(dp_04[i - 1][j],dp_04[i][j - 1]) + a_04[i][j];  //最短
            }
        }
    }
    cout<< dp_04[n][n] <<endl;

    return;
}



已知状态转移方程 ,求最大值

f[i][j] += max(f[i - 1][j],f[i - 1][j - 1]);
输入:
4
3
1 2
6 2 3
3 5 4 1


输出:
15


const int N_05 = 1e3 + 9;
const int inf_05 = 1000000000;  //1e9
int f_05[N_05][N_05];

void test_05() { //bug
    int n;
    cin >>n;  //行数
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= i; j++) {
            cin >> f_05[i][j];
        }
    }
    int ma = -inf_05;
    for(int i = 1; i <= n; i++) {
        for(int j = 1; i <= i; j++) {
            f_05[i][j] += max(f_05[i - 1][j],f_05[i - 1][j - 1]);
            if(i == n) {
                ma = max(f_05[i][j],ma);
            }
        }
    }
    if(ma == -inf_05) {
        ma = 0;
    }
    cout << ma <<endl;
    //cout << -inf_05 <<endl;
    return;
}

回家问题的递推式

f[i][j][k] = min ( min(f[i - 1][j][k] , f[i][j - 1][k]) ,f[i][j][k - 1] ) + f[i][j][k];

输入:
2 2 2
1 2 3
4 5 6
7 8 9
1 2 3
4 5 6
7 8 9
1 2 3
4 5 6
7 8 9

输出:
23


const int N_06 = 1e2 + 9;
const int inf_06 = 1000000000;  //1e9  //保证更新
int f_06[N_06][N_06][N_06];

void test_06() {
    int x,y,z;
    cin >> x >> y >> z;
    for(int i = 0; i <= x; i++) {
        for(int j = 0; j <= y ; j++) {
            for(int k = 0; k <= z; k++) {
                cin >> f_06[i][j][k];
            }
        }
    }

    for(int i = 0; i <= x; i++) {
        for(int j = 0; j <= y ; j++) {
            for(int k = 0; k <= z; k++) {
                int mi = inf_06; //保证更新
                if(i != 0) { //边界i == 0不能走 i - 1  , 即 i != 0的情况单独讨论,同理
                    mi = min(mi , f_06[i - 1][j][k]);
                }
                if(j != 0) {
                    mi = min(mi , f_06[i][j - 1][k]);
                }
                if(k != 0) {
                    mi = min(mi , f_06[i][j][k - 1]);
                }
                if(mi != inf_06) {
                    f_06[i][j][k] += mi;
                }
            }
        }
    }
    cout<< f_06[x][y][z] <<endl;

    return;
}

动态规划入门题

爬楼梯 (一次最多只能跨两个台阶  , 即1 或 2)

输入:
4

输出:
5


const int mod_07 = 100007;  //答案可能很大输出答案 对100007取模 的结果
int dp_07[1010];

void test_07() {
    int n;
    cin >> n;
    dp_07[1] = dp_07[0] = 1; //边界
    for(int i = 2; i <= n; i++) {
        dp_07[i] = (dp_07[i - 1] + dp_07[i - 2] )  % mod_07 ; //状态转移方程
    }
    cout<< dp_07[n] <<endl;

    return;
}


爬楼梯升级:每次可以跳跃任意奇数的台阶

问有多少种放法爬到楼顶

输入:
4

输出:
3


const int mod_08 = 100007;  //答案可能很大输出答案 对100007取模 的结果
int dp_08[1010];

void test_08() {
    int n;
    cin >> n;
    dp_08[0] = 1; //边界
    for(int i = 1; i <= n; i++) {
        dp_08[i] = 0;//初始值
        for(int j = i - 1; j >= 0; j -= 2) { //奇数阶
            dp_08[i] += dp_08[j] ; //每个遍历 从上往下降级 加组合
            dp_08[i] %= mod_08;
        }
    }
    cout<< dp_08[n] <<endl;

    return;
}

弹小球

输出一个整数,表示经过多少次才能弹出弹簧板

输入:
5
2 2 3 1 2

输出:
3


#include<cstring>
const int maxn_09 = 100010;
int a_09[maxn_09] , dp_09[maxn_09];

void test_09() {
    int n;
    cin >> n;
    memset(dp_09,0,sizeof(dp_09));  //清零 ,初始化           memset(数组名,赋值,数组字节大小)
    for(int i = 1; i <= n; i++) {
        cin >> a_09[i];
    }
    int ans = 0;
    for(int i = n; i >= 1; i--) {
        dp_09[i] = dp_09[i + a_09[i]] + 1;
        ans = max(ans , dp_09[i]);
    }
    cout << ans << endl;
    return;
}

传娃娃

n, m   n个人玩游戏 ,传m次娃娃

问有多少种传娃娃的方法?

输入:
3 3

输出:
2


int f_10[35][35];

void test_10() {
    int n,m;
    cin >> n >> m;
    memset(f_10,0,sizeof(f_10));  //清零 ,初始化           memset(数组名,赋值,数组字节大小)
    f_10[0][1] = 1;
    for(int i = 1; i <= m; i++) {
        for(int j = 1; j <= n; j++) {
            if(j == 1) {
                f_10[i][j] = f_10[i - 1][2] + f_10[i - 1][n];   //1号可以从上一次的传过来
            } else if(j == n) {
                f_10[i][j] = f_10[i - 1][1] + f_10[i - 1][n];
            } else {
                f_10[i][j] = f_10[i - 1][j - 1] + f_10[i - 1][j + 1];
            }
        }
    }
    cout<< f_10[m][1] << endl;

    return;
}


逃生-生命值


#include<cstdio>
const int inf_11 = 0x3f3f3f3f;
const int maxn_11 = 1010;
int a_11[maxn_11][maxn_11];
int dp_11[maxn_11][maxn_11];

void test_11() {
    int n,m,x,y,v,c; //n,m地图大小  , x,y初始位置 , v初始化血量 , c生命值上限
    cin >> n >> m>> x >> y >> v >> c;
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) {
            scanf("%d",&a_11[i][j]);
        }
    }
    int xx[4] = {-1,-1,1,1};
    int yy[4] = {-1,1,-1,1};
    for(int t = 0; t < 4; t++) {
        for(int i = x; i > 0 &&i <= n; i++) {
            for(int j = y; j > 0 && j <= m; j -= yy[t]) {
                if(i == x && j == y) {
                    dp_11[i][j] = v;
                } else if(i == x) {
                    dp_11[i][j] = min(c , dp_11[i][j + yy[t]] + a_11[i][j] );
                } else if(i == x) {
                    dp_11[i][j] = min(c , dp_11[i + xx[t]][j] + a_11[i][j] );
                } else {
                    dp_11[i][j] = min(c ,max( dp_11[i + xx[t]][j] ,dp_11[i][j + yy[t]]  ) ) + a_11[i][j] ;
                }
                if(dp_11[i][j] < 0) {
                    dp_11[i][j] = -inf_11; //死了
                }
            }
        }
    }
    int ans = max ( (dp_11[1][1],dp_11[1][m]) , max(dp_11[n][1],dp_11[n][m]) );
    if(ans <= 0) {
        cout << "-1" <<endl;
    } else {
        cout << ans <<endl;
    }
    return;
}


一维消消乐


n颗珠子排成一排 , 每一颗珠子的价值wi(可能是负数) 
可以选择若干对相邻的珠子 ,两个珠子消一次,且消去后还会占位 
输出最大分数 


输入:
8
-9 -5 -4 -2 4 -5 -4 2

输出:
73
 


const int maxn = 10010;
int dp[maxn][2];
int w[maxn];


void test_12() {
    int n;
    cin >> n;
    for(int i = 1; i <= n; i++) {
        cin >> w[i];
    }
    dp[1][0] = 0;
    for(int i = 2;i <= n;i++){
        dp[i][0] = max( dp[i - 1][0] ,dp[i - 1][1] );   //合 
        dp[i][1] =  dp[i - 1][0] + w[i] * w[i - 1] ; // 没合
    }
    cout<< max(    dp[n][0] ,     dp[n][1]) <<endl;
    return;
}

墙壁涂色


3种颜色 用(1/2/3表示) 
相邻颜色不相同 , 输入n,表示房间被划分成多少部分   ,输出有几种分法 
输入:
4

输出: 
18


typedef long long ll;
void test_13(){
    int n;
    ll a[51];
    a[1] = 3;
    a[2] = 6;
    a[3] = 6;
    for(int i = 4;i <= 50;i++){
        a[i] = a[i - 1] + a[i - 2] * 2;  //第n个 要考虑第n-1个是不是相同 
    }
    cin >> n;
    cout<< a[n] <<endl;
    return;
}

int main() {

    test_13();

    return 0;
}

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值