动态规划
无后效性,子问题重叠性,最优子结构性质,是问题能用动态规划求解的三个基本条件
无后效性:为了保证计算能够按顺序、不重复地进行,动态规划要求已经求解的子问题不受后续阶段的影响。
状态,阶段和决策是构成动态规划算法的三要素
动态规划是全局最优解
贪心是局部最优解
动态规划算法把相同的计算过程作用于各阶段的同类子问题,就好比,把一个公式在若干个输入数据上运行。六个字,状态转移方程!!!
线性动态规划经典例题:
LIS问题(最长上升子序列问题)
LCS问题(最长公共子序列问题)
P1439 【模板】最长公共子序列大佬发的题解很有意思
数字三角形
P1216 [USACO1.5] [IOI1994]数字三角形 Number Triangles
背包
01背包
例题:01背包
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1010;
ll n,m;
ll v[N],w[N];
ll res;
ll f[N][N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>v[i]>>w[i];
}
for(int i=n;i>=1;i--)
{
for(int j=0;j<=m;j++)
{
if(j < v[i])
{
f[i][j] = f[i+1][j];
}
else if(j >= v[i])
{
f[i][j] = max(f[i+1][j],f[i+1][j-v[i]]+w[i]);
}
}
}
cout<<f[1][m]<<"\n";
return 0;
}
优化空间,二维变一维
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1010;
ll n,m;
ll v[N],w[N];
ll res;
ll f[N];
ll dp[N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>v[i]>>w[i];
}
for(int i=n;i>=1;i--)
{
for(int j=0;j<=m;j++)
{
dp[j] = f[j];
if(j >= v[i])
{
dp[j] = max(f[j],f[j-v[i]]+w[i]);
}
}
memcpy(f,dp,sizeof f);
}
cout<<f[m]<<"\n";
return 0;
}
完全背包
例题:P1616 疯狂的采药
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e4+5;
const ll M = 1e7+5;
ll T,n;
ll t[N],w[N];
ll dp[M];
int main()
{
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 = t[i] ; j <= T; j++)
{
dp[j] = max(dp[j],dp[j - t[i]] + w[i]);
}
}
cout << dp[T];
return 0;
}
例题:P1164 小A点菜
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e2+5;
ll n,m;
ll a[N];
ll f[1005];
ll g[1005];
//ll mem[N][1005];
//ll dfs(ll x,ll spm)
//{
// if(mem[x][spm]) return mem[x][spm];
// ll sum = 0;
// if(x>n) sum = 0;
// else if(spm == a[x])
// {
// sum = dfs(x+1,spm)+1;
// }
// else if(spm > a[x])
// {
// sum = dfs(x+1,spm)+dfs(x+1,spm-a[x]);
// }
// else if(spm < a[x])
// {
// sum = dfs(x+1,spm);
// }
// mem[x][spm] = sum;
// return sum;
//}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
// ll res = dfs(1,m);
// cout<<res;
for(int i=n;i>=1;i--)
{
for(int j=0;j<=m;j++)
{
g[j] = f[j];
if(j == a[i])
{
g[j] = f[j] + 1;
}
else if(j > a[i])
{
g[j] = f[j] + f[j-a[i]];
}
else if(j < a[i])
{
g[j] = f[j];
}
}
memcpy(f , g , sizeof f);
}
cout<<f[m];
return 0;
}
//最长公共子序列问题 O(n^2)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e3+5;
ll n;
ll a[N];
ll b[N];
ll dp[N][N];
ll mx = -1;
int main()
{
cin >> n;
for(int i = 1;i <= n; i++)
{
cin >> a[i];
}
for(int i = 1; i <= n; i++)
{
cin >> b[i];
}
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
if(a[i] == b[j])
{
dp[i][j] = dp[i - 1][j - 1] + 1;
}
else
{
dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]);
}
}
}
cout << dp[n][n];
return 0;
}
滚动数组
代码1(交替滚动)
int dp[2][N];
int solve(int n , int c)
{
int now = 0 , old = 1; //now 指向当前正在计算的一行,old指向旧的一行
for(int i = 1; i <= n; i++)
{
swap(old, now); //交替滚动,now始终指向最新的一行
for(int j = 0; j <= c; j++)
{
if(j < a[i])
dp[now][j] = dp[old][j];
else
dp[now][j] = max(dp[old][j], dp[old][j - a[i]]+ w[i]);
}
}
return dp[now][c]; //返回最新的行
}
代码2(自我滚动)
int dp[N];
int solve(int n , int c)
{
for(int i = 1; i <= n; i++)
for(int j = c; j >= a[i]; j--)
{
dp[j] = max(dp[j],dp[j - a[i]] + w[i];
}
return dp[c];
}