动态规划和简单搜索模板

1.动态规划

在写动态规划问题的时候,请务必思考以下问题:

1.状态:变量有哪些

2.变化:状态是如何变化的

你可以将动态规划想成集合里面找最值的问题,然后努力找到状态转移方程。

在解答动态规划的问题的时候请反复问自己一个问题:上一个状态是什么,现在的状态是什么

1.1求最优解路径

dp[x][y][z]...状态
type dp(状态1,状态2,状态3){
	for(状态1的遍历)
		for(状态2的遍历)
			for(状态3的遍历){
				max=(上一个路径+这个点的值) 
			}
				 
} 

请注意,在边界的位置一定要初始化,求最大值则将边界初始化最小数(-1E9);求最小值则将边界初始化为最大值(1E9);

1.2最长上升子序列

不记录状态的朴素解法(数据过大容易超时)
for(int i=1;i<=n;i++) cin>>a[i];
    int res=0;
    for(int i=1;i<=n;i++){
        f[i]=1;//只要a[i]一个数
        for(int j=1;j<i;j++){
            if(a[j]<a[i]) f[i]=max(f[i],f[j]+1);
        }
        res=max(res,f[i]);
    }

记录状态的朴素算法
前面大致相同,唯一不同的是f[i]的条件
int a[N],f[n],g[n];
for(int i=1;i<=n;i++) cin>>a[i];
   for(int i=1;i<=n;i++){
        f[i]=1;//只要a[i]一个数
        g[i]=0;//表示第i个数没有从谁转移过来
        for(int j=1;j<i;j++){
            if(a[j]<a[i]) {
                if(f[i]<f[j]+1){
                    f[i]=f[j]+1;//记录f[i]是从谁转移过来的
                    g[i]=j;
                }
            }
        }
    }

    int k=0;//找到最大长度
    for(int i=1;i<=n;i++){
        if(f[k]<f[i]) k=i;
    } 
    
    cout<<f[k]<<endl;
    //找到符合条件的串,从后往前找
    while(f[k]){
        res.push_back(a[k]);
        k=g[k];
    }
    for(int i=res.size()-1;i>=0;i--) cout<<res[i]<<' ';//因为前面是从后向前找,所以现在反过来输出
    return 0;

}

二分解法(此法还可以求出递增序列数组)

思路:比最后一个大就插入,最后一个小就进行二分,找到梯度最小的位置

for(int i=0;i<n;i++) cin>>a[i];
int a[N],q[N];//a[N]存放数据,q[N]存放递增数组
q[0]=a[0];
int len=0;//len为递增序列长度
for(int i=1;i<n;i++){//记得从第二个开始
    if(a[i]>q[len])    q[++len]=a[i]
    else{
            二分开始
        int l=0,r=len;
        while(l<r){
            int mid = (l + r) >> 1;
                if (q[mid] >= a[i]) r= mid;
                else l = mid + 1; 
        }
                q[l] = a[i];
    }
}
cout<<len+1<<endl;//记得加1

1.3线性dp

1.4背包问题

01背包只有一个物体从大体积到当前物体体积
多重背包有多个物体从大体积到最小体积0
完全背包有无穷个物体从当前物体的体积慢慢增大到最大容量

 我们可以这样想:我们就是要把背包空间尽量用完为止。所以就有:

若当前体积不再放得下当前物品,那就应该停止内层循环(01背包)

for(j=V1;j>=v1;j--)

因为有多个物品,所以就得拿小东西来填补缝隙,所以体积就应该到>=0,放不下为止

 for(int j=m;j>=0;j--){
        for(int k=0;k*v<=j&&k<=s;k++){

既然有无限个,那就从能放得下第一块物品开始直接填补缝隙

 for(int j=v;j<=m;j++)

1.4.1 01背包问题

状态转移方程:dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+val[i]);
dp[i-1][j]:不选第i个,体积不大于j的情况
dp[i-1][j-v[i]]+val[i]:选第i个的情况(不选第i个,体积不大于j-第i个体积的情况+第i个物品价值的情况)
二维
int v[i],val[i];
初始化
for(int i=1;i<=n;i++){
    for(int j=1;j<=v0;j++{
        dp[i][j]=dp[i-1][j];
        if(j>=v[i])    dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+val[i]);
    }
}
cout<<dp[n][v0]<<endl;
一维01背包问题
cin>>V1>>V2>>n;
for(int i=0;i<n;i++){//因为dp实际只比较上一个,所以完全可以减少一个维度
                     //因为减少了一个维度,所以我们甚至可以不用数组来存储每个物品的价值

    int v1, v2,val;//花费1,花费2,价值
    cin >> v1 >> v2>>val;
    //所以只能从大到小,不能再从小到大
    for(j=V1;j>=v1;j--)//遍历{
    for(k=V2;j>=v2;j--){
            f[j][k]=max(f[j][k],f[j-v1][k-v2]+val)
}
}
}

1.4.2完全背包

完全背包问题:一维解法:
//完全背包问题是有无限个物品

int n,m;//n为个数,m为背包容量
cin>>n>>m;
for(int i=1;i<=n;i++){
    int v,w;
    cin>>v>>w;
    for(int j=v;j<=m;j++){//因为有无限个所以和01背包不一样,直接从当前体积慢慢增加
        dp[j]=max(dp[j],dp[j-v]+w);
    }

cout<<dp[m]<<endl;
    return 0;
}

 1.4.3多重背包

多重背包:一维解法:
多重背包的遍历和01背包思路相同,是让体积从大到小遍历。唯一不同的是体积的终止条件是>0。

int n,m;
cin>>n>>m;

for(int i=1;i<=n;i++){
    int v,w,s;
    cin>>v>>w>>s;
    for(int j=m;j>=0;j--){
        for(int k=0;k*v<=j&&k<=s;k++){
            f[j]=max(f[j],f[j-k*v]+k*w);
        }
    }
}
cout<<f[m]<<endl;
    return 0;

2.搜索

2.1.1Flood Fill

#include<bits/stdc++.h>
using namespace std;
/*三要素:图,遍历状态图,队列*/
#define x first
#define y second
typedef pair<int,int> PII;
const int N=;
int ma[N][N];
boolean con[N][N];
PII q[N*N];

void dfs(int ax,int ay){
/*初始化:
    初始化队列,初始化状态,创建头尾指针
*/
    int hh=0,tt=0;
    q[0]={ax,ay};
    con[ax][ay]=1;
/*队列中的点出队,遍历周围的点*/
    while(hh<=tt){
        //取出一个点开始遍历
        PII t=q[hh++];
        for(i=t.x-1;i<=t.x+1;i++){
            for(j=t.y-1;j<=t.y+1;j++){
                if(不符合的条件)    continue;//跳过

                /*将符合条件的点入队,更新遍历状态*/
                q[++tt]={i,j};
                con[i][j]=1;
            }
        }
    }
}

2.1.2最短路模型

/*此类题就是普通的bfs的思路,解题时需要外加一个pre数组记录前一个点即可,第三个条件判断是否遍历过*/

#include<bits/stdc++.h>
using namesapce std;

const int N=;
#define x first
#define y second
typedef pair<int,int> PII;
int ma[N][N];
PII q[N*N];
PII pre[N][N];
int n,m;//n,m为题目给定的图的长宽
void bfs(int ax,int ay){
    int hh=0,tt=0;
    int a[4]={},b[4]={};//走方向
    q[0]={ax,ay};
    memset(-1,pre,sizeof(pre));
    pre[0][0]=0;
    while(hh<=tt){
    PII t=q[hh++];
    for(int i=0;i<4;i++){
    int bx=t.x+a[i],by=t.y+b[i];
    if(bx<0||bx>=n||by<0||by>=m)    continue;
    if(不能通过的条件)    continue;
    if(pre[bx][by]!=-1)    continue;
    q[++tt]={bx,by};
    pre[bx][by]=t;//将前一个点计入
        }
    }    
}

int main(){
    for(int i=0;i<n;i++){
    for(int j=0;j<m;j++){
        cin>>ma[i][j];
    }    
}
    bfs(0,0)//输入起点坐标
    
    return 0;
}

2.1.3多源BFS

/*思路:把源头计入第一层,开始遍历并记录距离,唯一与BFS不同的就是距离要更新*/

#include<bits/stdc++.h>
using nemaspaces std;

#define x first
#define y second
typedef pair<int,int> PII;
const int N=;
int ma[N][N];
queue<PII> q[N*N];
int dist[N][N];

int n,m;//题目给出的图的大小
int a[4]={},b[4]={};//遍历周围哪些点
void bfs(){
    while(q.size()){
        PII t=q.front();
        q.pop();
        for(int i=0;i<4;i++){
            int ax=q.x+a[i],ay=q.y+b[i];
            if(ax<0||ax>=n||ay<0||ay>=m||ma[ax][ay]==1)    continue;
            /*更新距离*/
            dist[a][b] = dist[t.x][t.y] + 1;
            q.push({a,b});
            }
        }
    }
}
int main(){
    //传入源点,并把障碍标为1
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

艺成超爱牛肉爆大虾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值