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;
}