dp问题
01背包
#include<bits/stdc++.h>
using namespace std;
int n,m,f[1001],v[1001],w[1001];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>v[i]>>w[i];
for(int i=1;i<=n;i++)
{
for(int j=m;j>=v[i];j--)
f[j]=max(f[j-v[i]]+w[i],f[j]);
}
cout<<f[m];
}
完全背包
#include<bits/stdc++.h>
using namespace std;
int n,m,f[1001],v[1001],w[1001];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>v[i]>>w[i];
for(int i=1;i<=n;i++)
{
for(int j=v[i];j<=m;j++)
f[j]=max(f[j-v[i]]+w[i],f[j]);
}
cout<<f[m];
/*
f[i][j] = f[i - 1][j] + f[i - 1][j - i] + f[i - 1][j - 2 * i] + ...;
f[i][j - i] = f[i - 1][j - i] + f[i - 1][j - 2 * i] + ...;
因此 f[i][j]=f[i−1][j]+f[i][j−i];f[i][j]=f[i−1][j]+f[i][j−i]; (这一步类似完全背包的推导)
*/
}
多重背包(easy
#include<bits/stdc++.h>
using namespace std;
int n,m,f[1001],v[101],w[101],l[101];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>v[i]>>w[i]>>l[i];
for(int i=1;i<=n;i++)
for (int k = 1; k <= l[i];k++)
for (int j = m; j >= v[i]; j--)
f[j] = max(f[j - v[i]] + w[i], f[j]);
cout<<f[m];
}
多重背包(hard
#include<bits/stdc++.h>
using namespace std;
const int N=2001;
int n, m, f[N], v[N], w[N], l[N];
int qpow(int x,int k)
{
int ans=1;
while(k)
{
if(k&1) ans*=x;
x*=x;
k>>=1;
}
return ans;
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n;i++)
{
cin >> v[i] >> w[i] >> l[i];
}
for (int i = 1; i <= n;i++)
{
int res = l[i];
for (int k = 1; k <= res; res -= k, k *= 2)
{
for (int j = m; j >= v[i] * k;j--)
{
f[j] = max(f[j], f[j - v[i] * k] + w[i] * k);
}
}
for (int j = m; j >= v[i] * res;j--)
{
f[j] = max(f[j], f[j - v[i] * res] + w[i] * res);
}
}
cout << f[m] << endl;
return 0;
}
分组背包(优化版
#include<bits/stdc++.h>
using namespace std;
const int N=1001;
int n, m, f[N], v[N], w[N], l[N];
vector<int> idx[N];
int qpow(int x,int k)
{
int ans=1;
while(k)
{
if(k&1) ans*=x;
x*=x;
k>>=1;
}
return ans;
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n;i++)
{
int id;
cin >> id >> v[i] >> w[i];
idx[id].push_back(i);
}
for (int i = 1; i <= 1000;i++)//一共有几类
{
for (int j = m; j ;j--)//背包容量
for(auto k:idx[i])//每一类的物品
{
if(v[k]<=j)
f[j] = max(f[j], f[j - v[k]] + w[k]);
}
}
cout << f[m]<<endl;
return 0;
}//就是把组看成物品总数 先看第几类 再看当前类的所有物品(前提是满足背包容量
二维背包(优化
#include<bits/stdc++.h>
using namespace std;
const int N=101;
int n, m, k, f[N][N], v[N], w[N], l[N];
int qpow(int x,int k)
{
int ans=1;
while(k)
{
if(k&1) ans*=x;
x*=x;
k>>=1;
}
return ans;
}
int main()
{
cin >> n >> m >> k;
for (int i = 1; i <= n;i++)
cin >> v[i] >> w[i] >> l[i];
for (int i = 1; i <= n;i++)
{
for (int j = m; j ; j--)
{
for (int x = k; x ;x--)
{
f[j][x] = f[j][x];
if(x>=l[i]&&j>=v[i])
f[j][x] = max(f[j][x], f[j - v[i]][x - l[i]] + w[i]);
}
}
}
cout << f[m][k];
return 0;
}
二维背包(朴素
#include<bits/stdc++.h>
using namespace std;
const int N=101;
int n, m, k, f[N][N][N], v[N], w[N], l[N];
int qpow(int x,int k)
{
int ans=1;
while(k)
{
if(k&1) ans*=x;
x*=x;
k>>=1;
}
return ans;
}
int main()
{
cin >> n >> m >> k;
for (int i = 1; i <= n;i++)
cin >> v[i] >> w[i] >> l[i];
for (int i = 1; i <= n;i++)
{
for (int j = 0; j <= m; j++)
{
for (int x = 0; x <= k;x++)
{
f[i][j][x] = f[i - 1][j][x];
if(x>=l[i]&&j>=v[i])
f[i][j][x] = max(f[i][j][x], f[i - 1][j - v[i]][x - l[i]] + w[i]);
}
}
}
cout << f[n][m][k];
return 0;
}
01背包边界是 最后一个物品取或不取:取得话 就是f[i-1][j-v[i]]+w[i] 不取就是f[i-1][j]; 滚动数组实现 因为是一维 所以先小的被更改 但是要用到原先的数据 所以从大到小枚举
完全背包边界是 最后一个物品取或不取 取得话 就是f[i][j-v[i]]+w[i](第i个物品是多个 所以可以继续取 第一维应该还是i) 不取就是f[i-1][j]; 也是滚动数组实现 因为是一维 但是 边界是用的第i个 所以从小到大枚举
多重背包easy 就是 把1类物品多个 看成多个一样的物品 比如 x个1 那就是 1 1 1 1...1 然后当作01背包来写
多重背包hard 就是 借助二进制分配(一个数字 肯定能用一堆2的次幂的数字来表示 比如 3=1+2 9=1+2+4+2
循环枚举顺序 先是物品种类数 再枚举单个物品的个数(可以直接暴力 或者二进制分解) 最后按照体积来枚举(要看前面的数量) 看是从大到小 还是从小到大
最长上升子序列
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int a[N],f[N];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++)
{
f[i]=1;
for(int j=1;j<i;j++)
{
if(a[i]>a[j])
f[i]=max(f[i],f[j]+1);
}
}
int idx=-1;
for(int i=1;i<=n;i++)
idx=max(idx,f[i]);
cout<<idx;
return 0;//dp是在前面被计算的基础上继续做的 所以考虑转移方程的时候不用考虑之前的怎么算的 之前的就是通过现在的算出来的 递推来的
}
区间dp
所有的区间dp问题枚举时,第一维通常是枚举区间长度,并且一般 len = 1 时用来初始化,枚举从 len = 2 开始;第二维枚举起点 i (右端点 j 自动获得,j = i + len - 1)
for (int len = 1; len <= n; len++) { // 区间长度
for (int i = 1; i + len - 1 <= n; i++) { // 枚举起点
int j = i + len - 1; // 区间终点
if (len == 1) {
dp[i][j] = 初始值
continue;
}
for (int k = i; k < j; k++) { // 枚举分割点,构造状态转移方程
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + w[i][j]);
}
}