1.动态规划算法与分治法算法的区别:
dp经典题目:01背包
视频讲解:讲解视频(要开通活动中的基础算法题才能看)
文字讲解:这个讲的挺清楚传送门
下面的是原本的式子,没有优化,只有70分。
k(i,j) = KS(i-1,j-wi) + vi,K(i,j) = KS(i-1,j)
#include<bits/stdc++.h>
using namespace std;
const int N = 10000;
int v[N]; // 第 i 个物品的容量大小
int w[N]; // 第 i 个物品的价值大小
int f[N][N]; // f[i][j]: 总体积小于 j 下 在前 i 个物品可供挑选时的集合
int main()
{
int n, m;
cin >> n >> m; // n-物品的个数 m-背包容量大小
for(int i=1; i<=n; i++) cin >> v[i] >> w[i];
for(int i=1; i<=n; i++) // 第 i 个物品
for(int j=0; j<=m; j++) // 容量大小的遍历
if(j >= v[i]) f[i][j] = max(f[i-1][j], f[i-1][j-v[i]] + w[i]); //拿得下可以拿
else f[i][j] = f[i-1][j]; // 拿不下
cout << f[n][m];
return 0;
}
要优化空间,因为二维的会爆空间。
下面是优化过的满分
#include<bits/stdc++.h>
using namespace std;
const int N = 10000;
int v[N]; // 第 i 个物品的容量大小
int w[N]; // 第 i 个物品的价值大小
int f[N]; // f[i][j]: 在小于等于 j 体积下 只有前 i 个物品可供挑选时的最优选择的最大价值
int main()
{
int n, m;
cin >> n >> m;
for(int i=1; i<=n; i++) cin >> v[i] >> w[i];
for(int i=1; i<=n; i++) // 第 i 个物品
for(int j=m; j>=v[i]; j--) // 优化后这里
f[j] = max(f[j], f[j-v[i]]+w[i]);
/*
第二层循环倒过来的原因:https://www.bilibili.com/video/BV1Yq4y1m7vF?from=search&seid=8030297889955864038&spm_id_from=333.337.0.0
*/
cout << f[m];
return 0;
}
题解思路:https://www.luogu.com.cn/blog/user31798/solution-p1164
/*
对比下 01背包的理解就能解决疑惑的问题!!
f[i][j] = max(f[i-1][j], f[i-1][j-v[i]] + w[i]);
说明: 如果你先少拿第 i 个物品,j也减少一个 vi 那么这个时候的 f的值排名(二维图上的行)不变,你后面再加上当前物品的价值 w[i] 就是当前的值了
if(j>a[i]) f[i][j] = f[i-1][j] + f[i-1][j-w[i]];
那么先少点一个菜,那么方案数的排名不变,但是后面我要加上 当前的方案数数 f[i-1][j]
*/
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int w[N];
int f[N][N]; // f[i][j] : 在小于等于 j 元钱的情况下,在前 i 道菜中选择菜的集合 (=方案数)
int main()
{
int n, m; cin >> n >> m;
for(int i=1; i<=n; i++) cin >> w[i];
for(int i=1; i<=n; i++)
for(int j=0; j<=m; j++)
{
if(j < w[i]) f[i][j] = f[i-1][j];
if(j == w[i]) f[i][j] = f[i-1][j]+1;
if(j > w[i]) f[i][j] = f[i-1][j-w[i]] + f[i-1][j];
/*
f[i-1][j-w[i]] 吃下这道菜的方案数,不难理解,自己推下。
但是 f[i-1][j] 的方案数 可不能丢,题目还要求呢,一起加进来!
*/
}
cout << f[n][m];
return 0;
}
二维优化成一维后的代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int w[N];
int f[N]; // f[i][j] : 在小于等于 j 元钱的情况下,在前 i 道菜中选择菜的集合 (=方案数)
int main()
{
int n, m; cin >> n >> m;
for(int i=1; i<=n; i++) cin >> w[i];
for(int i=1; i<=n; i++)
for(int j=m; j>=w[i]; j--)
{
if(j == w[i]) f[j] = f[j]+1;
if(j > w[i]) f[j] = f[j-w[i]] + f[j];
}
cout << f[m];
return 0;
}
想法:如果写出了二维的代码,优化成一维的代码很容易,所以还得牢记住 01 背包的解题思路和如何优化成一维的方法
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int t[N], w[N];
int f[N][N]; // f[i][j] : 在小于等于 j 时间的情况下,在前 i 株草药的集合 (=总价值)
int main()
{
int T, N; cin >> T >> N;
for(int i=1; i<=N; i++) cin >> t[i] >> w[i];
for(int i=1; i<=N; i++)
for(int j=0; j<=T; j++)
if(j>=t[i]) f[i][j] = max(f[i-1][j], f[i-1][j-t[i]] + w[i]);
else f[i][j] = f[i-1][j];
cout << f[N][T];
return 0;
}
二维数组转一维数组:
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int t[N], w[N];
int f[N][N]; // f[i][j] : 在小于等于 j 时间的情况下,在前 i 株草药的集合 (=总价值)
int main()
{
int T, N; cin >> T >> N;
for(int i=1; i<=N; i++) cin >> t[i] >> w[i];
for(int i=1; i<=N; i++)
for(int j=0; j<=T; j++)
if(j>=t[i]) f[i][j] = max(f[i-1][j], f[i-1][j-t[i]] + w[i]);
else f[i][j] = f[i-1][j];
cout << f[N][T];
return 0;
}
动态规划,分治算法里最关键的问题其实是寻找原问题的子问题,
并写出递推表达式,
只要完成了这一步,
代码部分都是水到渠成的事情了。