【刷题笔记4】

本文介绍了如何通过递归和动态规划的方法解决斐波那契数列、兔子繁殖、苹果分法、棋盘格子路径、最长递增子序列、迷宫求解、称重问题以及全排列等计算机科学中的经典问题。
摘要由CSDN通过智能技术生成

动态规划题目汇总

斐波那契数列:1,1,2,3,5,8,13……
递归一把解决三类问题:1.数据定义是按照递归的(斐波那契数列)。2.问题解法是按递归算法实现的。 3.数据形式是按照递归形式定义的。
递归的一般形式:

void rec(形参列表)
{
	if(test) return;  //边界条件
    //!!!注意!!!  递归一定要有边界条件!!!否则就会死循环!!!
    rec(实参列表)     //递归调用
    语句序列2         //递归返回段(回溯)
}

  1. 有一种兔子,从出生后第3个月起每个月都生一只兔子,小兔子长到第三个月后每个月又生一只兔子。例:假设一只兔子第3个月出生,那么它第5个月开始会每个月生一只兔子。一月的时候有一只兔子,假如兔子都不死,问第n个月的兔子总数为多少?
    输入一个int型整数表示第n个月
    输出对应的兔子总数
#include <iostream>
using namespace std;
int total(int n);
int total(int n)
{
    if(n==1||n==2)//这个叫边界条件
    return 1;
    else
    return total(n-1)+total(n-2);//递归调用
}
int main() {
    //斐波那契数列
    int n,num;
    cin>>n;
    num=total(n);
    cout<<num;
}

  1. 把m个同样的苹果放在n个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法? 注意:如果有7个苹果和3个盘子,(5,1,1)和(1,5,1)被视为是同一种分法。

解题思路:
假设有m个苹果和n个盘子,我们可以将问题分为两种情况: 1. 盘子中至少有一个盘子为空:这种情况下,我们可以将m个苹果放在n-1个盘子中,即将问题转化为将m个苹果放在n-1个盘子中的分法。所以,这种情况下的分法数为f(m, n-1)。 2. 盘子中所有盘子都有苹果:这种情况下,我们可以将每个盘子中放入一个苹果,然后将剩余的m-n个苹果放在n个盘子中,即将问题转化为将m-n个苹果放在n个盘子中的分法。所以,这种情况下的分法数为f(m-n, n)。 综上所述,将m个苹果放在n个盘子中的分法数为f(m, n) = f(m, n-1) + f(m-n, n)。
边界条件: - 当m=0时,表示没有苹果需要放入盘子中,所以只有一种分法,即所有盘子都为空。 - 当n=0时,表示没有盘子可以放苹果,所以没有分法。 根据上述递推关系和边界条件,可以使用递归或动态规划的方法来求解。

#include <iostream>
using namespace std;

int fn(int m,int n)
{
    if(n<1) return 0;
    if(m<0) return 0;
    if(m==0) return 1;
    return fn(m-n,n)+fn(m,n-1);//没有空盘子+有1个空盘子
}

int main() {
    int m,n;
    cin>>m>>n;
    cout<<fn(m,n);
}

  1. n*m的棋盘格子(n为横向的格子数,m为竖向的格子数)从棋盘左上角出发沿着边缘线从左上角走到右下角,总共有多少种走法,要求不能走回头路,即:只能往右和往下走,不能往左和往上走。
#include <iostream>
using namespace std;
int digui(int x,int y)
{
    if(x==1||y==1)//只有一行或者一列的时候,就有x+y种方法
    return x+y;    //否则就往上或者往下走一步。
    return digui(x-1,y)+digui(x,y-1);

	//if(x==0)
	//return 1;
	//if(y==0)
	//return 1;
    //return x+y;    //否则就往上或者往下走一步。
    //return digui(x-1,y)+digui(x,y-1);




	//if(m<0||n<0)
    //return 0;
    //else if(n==1&&m==0)
    //return 1;
    //else if(m==1&&n==0)
    //return 1;
    //else  
    //return(digui(m-1, n)+digui(m,n-1));
}

int main() {
    int m,n;
    cin>>n>>m;
    cout<<digui(m,n);

}

最长递增子序列问题:

在这里插入图片描述
4.

class Solution {
  public:
    int LIS(vector<int>& arr) {
        if (arr.empty())
            return 0;
        int N = arr.size();
        vector<int>dp(N, 1);
        int maxLen = 1;
        for (int i = 1; i < N; i++) {
            for (int j = 0; j < i; j++) {
                if (arr[i] > arr[j]) {
                    dp[i] = max(dp[i], dp[j] + 1); //长度加1
                }
            }
            maxLen = max(maxLen, dp[i]);//更新最大长度
        }
        return maxLen;
    }
 
};

在这里插入图片描述

  1. BFS(广度优先搜索算法)
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
int Map[5][5];  //定义地图大小
int dir[4][2]= {1,0,-1,0,0,-1,0,1};  //定义方向
int n,m,ans;
struct node
{
    int x,y,step;

} now,nextt;  //保存走步
int BFS(int x,int y)
{
    queue<node>q;
    int xx,yy,zz;
    Map[x][y]=2;  //走过初始点
    now.x=x;
    now.y=y;
    now.step=0;
    q.push(now);  //从当前点开始
    while(!q.empty())
    {
        now=q.front();
        q.pop();
        for(int i=0; i<4; i++)  //遍历四个方向
        {
            xx=now.x+dir[i][0];
            yy=now.y+dir[i][1];  //走一步
            if(xx>=0&&xx<5&&yy>=0&&yy<5&&Map[xx][yy]!=1&&Map[xx][yy]!=2)  //可以走
            {
                nextt.x=xx;
                nextt.y=yy;
                nextt.step=now.step+1;  //步数加一
                Map[now.x][now.y]=2;   //走过一个点
                if(Map[xx][yy]==3)  //到达终点
                    return nextt.step;
                q.push(nextt);

            }
            for(int i=0; i<5; i++){      //打印地图
                for(int j=0; j<5; j++)
                    cout << Map[i][j];
                cout << endl;
            }
            cout << endl;
        }
    }
    return -1;   //走不过去
}

int main()
{

    for(int i=0; i<5; i++)     //输入地图
        for(int j=0; j<5; j++)
            cin >> Map[i][j];
    Map[4][4]=3;  //定义终点
    ans=BFS(0,0);
    cout << ans<< endl;
    return 0;

}


自己重构的走迷宫的代码:

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
using namespace std;
int n,m;
int zuiyou;
vector<vector<int>>maze;  //定义地图大小
struct point{
    int x,y;
};
int fangxiang[4][2]={{-1,0},{1,0},{0,-1},{0,1}};  //定义上下左右
vector<vector<int> >path(5,vector<int>(5));//记录每次的方向,0,1,2,3对应上下左右
vector<vector<int>>fan;

struct node{
    int x,y,z;
}now, nextt;;

void bfs(int x,int y)
{   
    // vector<vector<int>> path(n, vector<int>(m));
    maze[x][y]=2;
    queue <node> q;
    now.x=x;
    now.y=y;
    now.z=0;
    q.push(now);
    
    while(!q.empty())
    {   
        now=q.front();
        maze[now.x][now.y]=2;
        q.pop();
        for( int i=0;i<4;i++)
        {
            int xx=now.x+fangxiang[i][0];
            int yy=now.y+fangxiang[i][1];
            if(xx>=0&&xx<n&&yy>=0&&yy<m&&maze[xx][yy]==3)
            {   
                path[xx][yy]=i;
                if(maze[xx][yy]==3)
                    zuiyou=now.z+1;
            }

            if(xx>=0&&xx<n&&yy>=0&&yy<m&&maze[xx][yy]==0)
            {   
                nextt.x=xx;
                nextt.y=yy;
                nextt.z=now.z+1;
                q.push(nextt);
                path[xx][yy]=i;
            }
        }
    }

    int ii=n-1,jj=m-1;
    fan.push_back({ii,jj});

    while(ii!=0||jj!=0)
    {   
        int num=path[ii][jj];
        ii=ii-fangxiang[num][0];   
        jj=jj-fangxiang[num][1];   
        fan.push_back({ii,jj});
    }
    reverse(fan.begin(),fan.end());
}


int main()
{   
    cin>>n>>m;
    maze=vector<vector<int>>(n,vector<int>(m));  //定义地图大小    
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            cin>>maze[i][j];
        }
    }
    maze[4][4]=3;
    bfs(0, 0);
    cout<<zuiyou<<endl;;
    cout<<endl;
    for(auto t:fan)
    {
        cout<<t[0]<<","<<t[1];
        cout<<endl;
    }


}



一个非常简单好用的方法,解决称砝码问题:
现有n种砝码,重量互不相等,分别为 m1,m2,m3…mn ;每种砝码对应的数量为 x1,x2,x3…xn 。现在要用这些砝码去称物体的重量(放在同一侧),问能称出多少种不同的重量。

#include <algorithm>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <vector>
using namespace std;

int main()
{
    int n;
    cin>>n;
    vector<int>a;
    vector<int>b;
    for(int i=0;i<n;i++)
    {   
        int aa;
        cin>>aa;
        a.push_back(aa);
    }

    for(int i=0;i<n;i++)
    {   
        int aa;
        cin>>aa;
        b.push_back(aa);
    }

    set<int>s,tmp;
    s.insert(0);
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<b[i];j++)
        {
            tmp=s;
            for(set<int>::iterator it=s.begin();it!=s.end();it++)
            tmp.insert(a[i]+*it);
            s=tmp;
        }

    }
    cout<<s.size();
}

在这里插入图片描述

  1. DFS(深度优先搜索算法):一般用于查找图中的路径、连通性检测、拓扑排序等。
    深度优先搜索使用栈(Stack)数据结构来保存需要探索的节点。每次访问一个节点时,将其标记为已访问,并将其未访问的邻居节点压入栈中。然后从栈中弹出一个节点,继续访问该节点的未访问邻居节点,直到栈为空。

如果需要找到最短路径,可以考虑使用其他算法,如广度优先搜索(BFS)或 Dijkstra 算法。一般最小步数、最短距离、最小操作次数等问题采用BFS。


//DFS
#include <deque>
#include <iostream>
#include <queue>
#include <utility>
#include <vector>
using namespace std;

int N,M;
vector<vector<int>> maze;
vector<vector<int>> path_best;
vector<vector<int>> path_temp;//临时的辅助路径

void bestpath(int i,int j){
    //如果进来,走过就不走了
    maze[i][j]=1;
    
    //存进去
    path_temp.push_back({i,j});

    //判断出去的条件
    if(i==N-1&&j==M-1){
        if(path_temp.size()<path_best.size() || path_best.empty()){
            path_best = path_temp;
            return;
        }
    }

    //开始走 上、下、左、右
    if(i-1>=0 && maze[i-1][j]==0){
        bestpath(i-1,j);
    }
    if(i+1< N && maze[i+1][j]==0){
        bestpath(i+1,j);
    }
    if(j-1>=0 && maze[i][j-1]==0){
        bestpath(i,j-1);
    }
    if(j+1<M && maze[i][j+1]==0){
        bestpath(i,j+1);
    }

    //如果没进循环,标记回去
    maze[i][j]=0;
    path_temp.pop_back();//删除最后一个元素
}

int main() {
    while (cin >> N >> M) { // 注意 while 处理多个 case
        //赋值
        maze = vector<vector<int>>(N,vector<int>(M,0));
        //清空
        path_best.clear();
        path_temp.clear();
        for(int i=0;i<N;i++){
            for(int j=0;j<M;j++){
                cin>>maze[i][j];
            }
        }

        //递归回溯
        bestpath(0,0);

        //输出
        for(auto itor:path_best){
            cout<<"("<<itor[0]<<","<<itor[1]<<")"<<endl;
        }
    }
    return 0;
}



  1. 回溯

三步走:

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}

在这里插入图片描述

例:从输入数据里取出特定个数的数据的组合。

#include <iostream>
#include <vector>
using namespace std;
vector<int>path;
vector<vector<int>>res;

void dfs(vector<int>& v1,int k,int step,int index)
{
    if(step==k)
    {
        res.push_back(path);
        return;
    }
    
    for(int i=index;i<v1.size();i++)
    {
        path.push_back(v1[i]);
        step++;
        dfs(v1,k,step,i+1);
        step--;
        path.pop_back();
    }
}

int main() {
    vector<int>v1;
    int k;
    while(cin>>k)
    v1.push_back(k);
    v1.pop_back();
    int step=0;
    int index=0;
    dfs(v1,k,step,index);
    cout<<res.size()<<endl;
    for(auto t:res)
    {
        for(int i=0;i<t.size();i++)
        {
            cout<<t[i]<<" ";
        }
        cout<<endl;
    }
}

练习:全排列

#include <iostream>
#include <vector>
#include<algorithm>
using namespace std;
vector<int>path;
vector<vector<int>>res;
vector<int>jilu;

void dfs(vector<int>& v1,int num,int step,int index,vector<int>& jilu)
{
    if(step==num)
    {
        res.push_back(path);
        return;
    }
    for(int i=0;i<v1.size();i++)
    {
        if( find (jilu.begin(),jilu.end(), i)==jilu.end() )
        {
            path.push_back(v1[i]);
            jilu.push_back(i);
            step++;
            dfs(v1,num,step,i+1,jilu);
            step--;
            path.pop_back();
            jilu.pop_back();
        }
        
    }
}

int main() {
    vector<int>v1;
    int k;
    while(cin>>k)
    v1.push_back(k);
    int num=v1.size();
    int step=0;
    int shi=0;
    dfs(v1,num,step,shi,jilu);
    cout<<res.size()<<endl;

    for(auto t:res)
    {
        for(auto tt:t)
        {
            cout<<tt<<" ";
        }
        cout<<endl;
    }
}



  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值