背包问题
01背包问题
static const int N = 1010;
int dp[N][N], v[N], w[N], n, c;
int main(){
cin >> n >> c;
for(int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i];
for(int i = 1; i <= n; i ++ )
for(int j = 1; j <= c; 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]] + w[i]);
}
cout << dp[n][c];
return 0;
}
int dp[N], v[N], w[N], n, c;
int main(){
cin >> n >> c;
for(int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i];
for(int i = 1; i <= n; i ++ )
for(int j = c; j >= v[i]; j -- ) //需要倒序
dp[j] = max(dp[j], dp[j - v[i]] + w[i]); //保证是从上一层的数据过来的
//如果正序 对于某个容量j,小于j的容量的dp值已经被更新,此时已经不再是上一层的了
cout << dp[c];
return 0;
}
完全背包问题
static const int N = 1010;
int dp[N][N], v[N], w[N], n, c;
int main(){
cin >> n >> c;
for(int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i];
for(int i = 1; i <= n; i ++ )
for(int j = 0; j <= c; j ++ ){
dp[i][j] = dp[i - 1][j];
if(j >= v[i]) dp[i][j] = max(dp[i][j], dp[i][j - v[i]] + w[i]);
}
/*
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - v] + w, dp[i - 1][j - 2 * v] + 2 * w ... )
dp[i][j - v] = max( dp[i - 1][j - v], dp[i - 1][j - 2 * v] + w ... )
所以
dp[i][j] = max(dp[i - 1][j], dp[i][j - v] + w)
*/
cout << dp[n][c];
return 0;
}
//一维数组版本
int dp[N], v[N], w[N], n, c;
int main(){
cin >> n >> c;
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 <= c; j ++ )
dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
cout << dp[c];
return 0;
}
分组背包问题
static const int N = 110;
int dp[N], v[N], w[N];
int n, c;
int main(){
cin >> n >> c;
for(int i = 1; i <= n; i ++ ){
int cnt; cin >> cnt;
for(int j = 1; j <= cnt; j ++ ) cin >> v[j] >> w[j];
for(int j = c; j >= 0; j -- )
for(int k = 1; k <= cnt; k ++ ) //对于每个容量 每组只能选择一个 所以外循环为容量 内循环为物品
if(j >= v[k]) dp[j] = max(dp[j], dp[j - v[k]] + w[k]);
}
cout << dp[c];
return 0;
}
多重背包
const int N = 110;
int dp[N], n, c;
int main(){
cin >> n >> c;
int v, w, s;
while(n -- ){
cin >> v >> w >> s;
for(int j = c; j >= 0; j -- )
for(int k = 0; k <= s && k * v <= j; k ++ )
dp[j] = max(dp[j], dp[j - k * v] + k * w);
}
cout << dp[c];
}
二维费用背包问题
const int N = 110;
int dp[N], n, c;
int main(){
cin >> n >> c;
int v, w, s;
while(n -- ){
cin >> v >> w >> s;
for(int j = c; j >= 0; j -- )
for(int k = 0; k <= s && k * v <= j; k ++ )
dp[j] = max(dp[j], dp[j - k * v] + k * w);
}
cout << dp[c];
}
混合背包问题
#include<iostream>
using namespace std;
const int N = 1010;
int n, c, v, w, s;
int dp[N];
int main(){
cin >> n >> c;
while(n -- ){
cin >> v >> w >> s;
if(!s){
for(int j = v; j <= c; j ++ )
dp[j] = max(dp[j], dp[j - v] + w);
}else if(s){
if(s == -1) s = 1;
for(int k = 1; k <= s; k <<= 1){
for(int j = c; j >= k * v; j -- )
dp[j] = max(dp[j], dp[j - k * v] + k * w);
s -= k;
}
if(s){
for(int j = c; j >= s * v; j -- )
dp[j] = max(dp[j], dp[j - s * v] + s * w);
}
}
}
cout << dp[c] << endl;
return 0;
}
数字三角形模型
for(int j = 0; j < n; j++)
dp[n - 1][j] = a[n - 1][j];
for(int i = n - 2; i >= 0; i--)
for(int j = 0; j < n; j++)
dp[i][j] = max(dp[i + 1][j], dp[i + 1][j + 1]) + a[i][j];
cout << dp[0][0];
子序列
最长上升子序列模型
int ans = 0;
for(int j = 1; j <= n; j++){
dp[j] = 1;
for(int i = 1; i < j; i++){
if(a[i] < a[j])
dp[j] = max(dp[j], dp[i] + 1);
}
ans = max(ans, dp[j]);
}
cout << ans;
最长公共子序列模型
for(int i = 1; i <= n; i ++ )
for(int j = 1; j <= m; j ++ )
if(s[i] == t[j]) dp[i][j] = dp[i - 1][j - 1] + 1;
else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
cout << dp[n][m];
最长公共上升子序列模型
for(int i = 1; i <= n; i ++ ){
int maxv = 1;
for(int j = 1; j <= n; j ++ ){ //枚举结尾
dp[i][j] = dp[i - 1][j];
if(a[i] == b[j]) dp[i][j] = max(dp[i][j], maxv);
if(a[i] > b[j]) maxv = max(maxv, dp[i - 1][j] + 1);
}
}
int res = 0;
for(int i = 1; i <= n; i ++ ) res = max(res, dp[n][i]);
cout << res;
子序列匹配个数模型
for(int i = 0; i <= m; i ++ ) dp[i][n] = 1;
for(int i = m - 1; i >= 0; i -- )
for(int j = n - 1; j >= 0; j -- )
if(s[i] == t[j]) dp[i][j] = (dp[i + 1][j + 1] + dp[i + 1][j]) % mod;
else dp[i][j] = dp[i + 1][j] % mod;
cout << dp[0][0] % mod << endl;
最大子段和模型
cin >> n;
for(int i = 1; i <= n; i ++ ) cin >> a[i];
sum = 0;
for(int i = 1; i <= n; i ++ ) //最大子段和模型
sum = max(0, sum) + a[i], dp1[i] = max(dp1[i - 1], sum);
sum = 0;
for(int i = n; i; i -- )
sum = max(0, sum) + a[i], dp2[i] = max(dp2[i + 1], sum);
区间DP
石子合并模型
cin >> n;
for(int i = 1; i <= n; i++) cin >> s[i], s[i] += s[i - 1];
for(int len = 2; len <= n; len ++ ){
for(int i = 1; i + len - 1 <= n; i ++ ){
int j = i + len - 1;
dp[i][j] = INT_MAX;
for(int k = i; k < j; k ++ )
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + s[j] - s[i - 1]);
}
}
cout << dp[1][n];
环形石子合并模型
cin >> n;
for(int i = 1; i <= n; i ++ ){
cin >> a[i];
a[n + i] = a[i]; //化环为链
}
for(int i = 1; i <= 2 * n - 1; i ++ ) pre[i] = pre[i - 1] + a[i];
for(int len = 1; len <= n; len ++ ){
for(int i = 1; i <= 2 * n - len - 1; i ++ ){
int j = i + len;
dp1[i][j] = inf;
for(int k = i; k < j; k ++ ){
dp1[i][j] = min(dp1[i][j], dp1[i][k] + dp1[k + 1][j] + pre[j] - pre[i - 1]);
dp2[i][j] = max(dp2[i][j], dp2[i][k] + dp2[k + 1][j] + pre[j] - pre[i - 1]);
}
}
}
int resmin = inf, resmax = 0;
for(int i = 1; i <= n; i ++ ){
resmin = min(resmin, dp1[i][i + n - 1]);
resmax = max(resmax, dp2[i][i + n - 1]);
}
cout << resmin << endl << resmax;
环形石子合并模型 + 最优分割点标记
for(int i = 1; i <= n; i ++ ){
cin >> a[i];
a[n + i] = a[i];
s[i][i] = i, s[n + i][n + i] = n + i;
dp[i][i] = dp[n + i][n + i] = 0;
}
for(int i = 1; i <= 2 * n; i ++ ) pre[i] = pre[i - 1] + a[i];
for(int len = 1; len <= n; len ++ ){
for(int i = 1; i <= 2 * n - len; i ++ ){
int j = i + len;
for(int k = s[i][j - 1]; k <= s[i + 1][j]; k ++ ){
if(dp[i][j] > dp[i][k] + dp[k + 1][j] + pre[j] - pre[i - 1]){
dp[i][j] = dp[i][k] + dp[k + 1][j] + pre[j] - pre[i - 1];
s[i][j] = k;
}
}
}
}
int res = 1 << 30;
for(int i = 1; i <= n + 1; i ++ )
res = min(res, dp[i][i + n - 1]);
cout << res << endl;
记忆化搜索
典型示例 滑雪
const int N = 310;
int n, m, f[N][N], g[N][N];
int dx[4] = {1, -1, 0, 0};
int dy[4] = {0, 0, 1, -1};
int dp(int i, int j){
int &v = f[i][j];
if(v != -1) return v;
v = 1;
for(int k = 0; k < 4; k ++ ){
int ni = i + dx[k], nj = j + dy[k];
if(ni >= 1&& ni <= n&& nj >= 1&& nj <= m && g[ni][nj] < g[i][j])
v = max(v, dp(ni, nj) + 1);
}
return v;
}
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i ++ )
for(int j = 1; j <= m; j ++ )
cin >> g[i][j];
memset(f, -1, sizeof(f));
int ans = 0;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
ans = max(ans, dp(i, j));
cout << ans;
}
状态压缩DP
//状态压缩DP + 滚动数组优化(Acwing 4406.积木画)
typedef long long ll;
static const int mod = 1e9 + 7;
ll dp[2][3]; //dp[i][j]表示前i列填满并且填了第i+1列j个位置的方案
//第i种状态只与i-1有关,可以用滚动数组优化
int main(){
int n; cin >> n;
dp[1][0] = 1, dp[1][1] = 2, dp[1][2] = 1;
for(int i = 2; i <= n; i ++ ){
dp[i & 1][0] = (dp[i - 1 & 1][0] + dp[i - 1 & 1][2]) % mod;
dp[i & 1][1] = (dp[i - 1 & 1][0] * 2 + dp[i - 1 & 1][1]) % mod;
dp[i & 1][2] = (dp[i - 1 & 1][0] + dp[i - 1 & 1][1]) % mod;
}
cout << dp[n & 1][0] << endl;
}
树形DP
没有上司的舞会 模型
const int N = 6010;
int happy[N], n;
int h[N], e[N], ne[N], idx;
int dp[N][2]; //dp[u][0]表示在以u为根的子树中选择并且不选u,dp[u][1]表示...并且选择u
bool hasfa[N];
void add(int a, int b){
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
void dfs(int u){
dp[u][1] = happy[u];
for(int i = h[u]; i != -1; i = ne[i]){
int j = e[i];
dfs(j);
dp[u][0] += max(dp[j][0], dp[j][1]);
dp[u][1] += dp[j][0];
}
}
int main(){
cin >> n;
for(int i = 1; i <= n; i++) cin >> happy[i];
memset(h, -1, sizeof(h));
for(int i = 0; i < n - 1; i ++ ){
int a, b; cin >> a >> b;
hasfa[a] = true;
add(b, a);
}
int root = 1;
while(hasfa[root]) root++;
dfs(root);
cout << max(dp[root][0], dp[root][1]);
}
树上最远距离 模型
static const int N = 10010, M = 2 * N;
int h[N], e[M], ne[M], w[M], idx;
int dp[N][2]; //dp[i][0]表示i出发的最长路,dp[i][1]表示i出发的次长路
int n;
int res = -1e9;
void add(int a, int b, int c){
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++ ;
}
void dfs(int u, int fa){
dp[u][0] = dp[u][1] = 0;
for(int i = h[u]; i != -1; i = ne[i]){
int v = e[i], c = w[i];
if(v == fa) continue;
dfs(v, u); //自底向上
if(dp[v][0] + c > dp[u][0]) dp[u][1] = dp[u][0], dp[u][0] = dp[v][0] + c;
else if(dp[v][0] + c > dp[u][1]) dp[u][1] = dp[v][0] + c;
}
res = max(res, dp[u][0] + dp[u][1]);
}
int main(){
cin >> n;
memset(h, -1, sizeof(h));
for(int i = 1; i <= n - 1; i ++ ){
int u, v, c; cin >> u >> v >> c;
add(u, v, c), add(v, u, c);
}
dfs(1, -1);
cout << res;
return 0;
}
树上最大和模型
static const int N = 1e5 + 10, M = 2 * N;
int h[N], e[M], ne[M], w[N], idx;
int n;
typedef long long ll;
ll dp[N];
void add(int a, int b){
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
void dfs(int u, int fa){
dp[u] = (ll)w[u];
for(int i = h[u]; i != -1; i = ne[i]){
int v = e[i];
if(v == fa) continue;
dfs(v, u); //自底向上
dp[u] += max(0LL, dp[v]);
}
}
int main(){
memset(h, -1, sizeof(h));
cin >> n;
for(int i = 1; i <= n; i ++ ) cin >> w[i];
for(int i = 1; i <= n - 1; i ++ ){
int u, v; cin >> u >> v;
add(u, v), add(v, u);
}
dfs(1, -1);
ll res = -1e12;
for(int i = 1; i <= n; i ++ ) res = max(res, dp[i]);
cout << res;
return 0;
}
标签:idx,int,max,cin,--,算法,模板,动态,dp
From: https://www.cnblogs.com/MAKISE004/p/17318072.html