动态规划学习笔记——2019.10.2

一、数列问题

二、背包问题

1、01背包

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 10;
int v[maxn], c[maxn];
int dp[maxn];//背包容量为i的最大价值 
int main() {
	int cmax, n;
	cin >> cmax >> n; 
	for(int i = 1; i <= n; i++) cin >> c[i];
	for(int i = 1; i <= n; i++) cin >> v[i]; 
	for(int i = 1; i <= n; i++) {
		for(int j = cmax; j >= c[i]; j--) {
			dp[j] = max(dp[j], dp[j - c[i]] + v[i]);
		}
	}
	cout << dp[cmax] << endl;
	return 0;
}

2、完全背包

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 10;
int c[maxn], v[maxn];
int dp[maxn];
int main() {
	int cmax, n;
	cin >> cmax >> n;
	for(int i = 1; i <= n; i++) cin >> c[i];
	for(int i = 1; i <= n; i++) cin >> v[i];
	for(int i = 1; i <= n; i++) {
		for(int j = c[i]; j <= cmax; j++) {
			for(int k = 0; k * c[i] <= j; k++) {
				dp[j] = max(dp[j], dp[j - k * c[i]] + k * v[i]);
			}
		}
	}
	cout << dp[cmax];
	return 0;
}
/*
10 4
2 3 4 7
1 3 5 9
*/

~优化2维

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 10;
int c[maxn], v[maxn], dp[maxn];
int main() {
	int cmax, n;
	cin >> cmax >> n;
	for(int i = 1; i <= n; i++) cin >> c[i];
	for(int i = 1; i <= n; i++) cin >> v[i];
	for(int i = 1; i <= n; i++) {
		for(int j = c[i]; j <= cmax; j++) {
			dp[j] = max(dp[j], dp[j - c[i]] + v[i]);
		} 
	}
	cout << dp[cmax];
	return 0;
}

3、多重背包

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 10;
int c[maxn], v[maxn], num[maxn];
int dp[maxn];
int main() {
	int cmax, n;
	cin >> cmax >> n;
	for(int i = 1; i <= n; i++) cin >> c[i];
	for(int i = 1; i <= n; i++) cin >> v[i];
	for(int i = 1; i <= n; i++) cin >> num[i];
	for(int i = 1; i <= n; i++) {
		for(int j = cmax; j >= c[i]; j--) {
			for(int k = 0; k * num[i] <= j; k++) {
				dp[j] = max(dp[j], dp[j - k * c[i]] + k * v[i]);
			}
		}
	}
	for(int i = 1; i <= cmax; i++) cout << dp[i] << " ";
	cout << endl; 
	cout << dp[cmax];
	return 0;
} 
/*
20 4
9 9 4 1
3 5 9 8
3 1 2 3
*/ 

三、区间类动态规划

特点:每次处理一个区间,并将剩下的区间合并起来

1、P1880 石子合并

#include <bits/stdc++.h>
using namespace std;
const int inf = 1e6 + 10;
const int maxn = 205;
int a[maxn];
int dp1[maxn][maxn], dp2[maxn][maxn];//分别表示最小得分和最大得分 
int sum[maxn];
int main() {
	int n; cin >> n;
	for(int i = 1; i <= n; i++) cin >> a[i];
	for(int i = 1; i <= n; i++) {
		a[i + n] = a[i];//处理环 
	}
	for(int i = 1; i <= n * 2; i++) {
		sum[i] = sum[i - 1] + a[i];
	}
	for(int l = 2; l <= n; l++) {//枚举合并的堆数 
		for(int i = 1; i <= 2 * n - l + 1; i++) {//枚举起点 
			int j = i + l - 1;//终点
			dp1[i][j] = 0x7fffffff; dp2[i][j] = 0;
			for(int k = i; k < j; k++) {//枚举中间位置 
				dp1[i][j] = min(dp1[i][j], dp1[i][k] + dp1[k + 1][j]);//合并与不合并的最值 
				dp2[i][j] = max(dp2[i][j], dp2[i][k] + dp2[k + 1][j]);
			} 
			dp1[i][j] = dp1[i][j] + sum[j] - sum[i - 1];
			dp2[i][j] = dp2[i][j] + sum[j] - sum[i - 1];
		}
	}
	int ans1 = 0x7fffffff, ans2 = 0;
	for(int i = 1; i <= n; i++) ans1 = min(ans1, dp1[i][i + n - 1]);
	for(int i = 1; i <= n; i++) ans2 = max(ans2, dp2[i][i + n - 1]);
	cout << ans1 << endl << ans2 << endl;
	return 0;
} 

四、树形动态规划

1、分为左子树与右子树

P2015 二叉苹果树

/此代码有问题,待修改/

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 505;
int mapp[maxn][maxn];//表示i到j的苹果数 
ll l[maxn], r[maxn];//第i点的左子树与右子树 
ll a[maxn];//a[i]为i节点到其父亲的边权值 
ll dp[maxn][maxn];//表示以i为根节点的数上保留j个节点的最大权值 
ll n, q;
void addtree(int v) {//建左子树与右子树 
    for(int i = 1; i <= n; i++) {
        if(mapp[v][i] != -1) {
            l[v] = i; a[i] = mapp[v][i];
            mapp[v][i] = -1; 
            mapp[i][v] = -1;
            addtree(i);
            break;
        }
    }
    for(int i = 1; i <= n; i++) {
        if(mapp[v][i] != -1) {
            r[v] = i; a[i] = mapp[v][i];
            mapp[v][i] = -1; 
            mapp[i][v] = -1;
            addtree(i);
            break;
        }
    }
}
int DP(int i, int j) {
    if(j == 0) return 0; 
	if((l[i] == 0) && (r[i] == 0)) return a[i];//如果左右子节点都没有,代表这个点为一个叶子节点 
    if(dp[i][j] != 0) return dp[i][j];//已经计算
    for(int k = 0; k <= j - 1; k++) {
        dp[i][j] = max(dp[i][j], DP(l[i], k) + DP(r[i], j - k - 1) + a[i]);
        //比较剪掉这条边和不剪这条边的最大值 
        return dp[i][j];
    } 
} 
int main() {
    cin >> n >> q;
    q++;//保留的点数 = 保留的边数 + 1 
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) {
            mapp[i][j] = -1;//初始化 
        } 
    } 
    for(int i = 1; i <= n - 1; i++) {
        int x, y, z;
        cin >> x >> y >> z;
        mapp[x][y] = z; mapp[y][x] = z;//双向 
    }
    addtree(1);
    cout << DP(1, q) << endl;
}

2、背包类树型DP

P2014 选课

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1005;
vector <int> son[maxn];//把选课的先修后修关系转化为图 
#define ll long long
ll dp[maxn][maxn];//dp[i][j]表示在i为根的子树中选j门课的最大学分 
ll s[maxn];
int m, n;
void DP(int x) {
	dp[x][0] = 0;
	for(int i = 0; i < son[x].size(); i++) {
		int y = son[x][i];
		DP(y);
		for(int t = m; t >= 0; t--)//循环遍历当前课程总数,即背包容量
		for(int j = t; j >= 0; j--) 
			dp[x][t] = max(dp[x][t], dp[x][t - j] + dp[y][j]); 
	}
	if(x != 0) {//x不为0时,选修x本身需要占用1们课,并获得相应学分 
		for(int t = m; t > 0; t--)
		dp[x][t] = dp[x][t - 1] + s[x];
	}
}
int main() {
	cin >> n >> m;
	for(int i = 1; i <= n; i++) {
		int x; 
		cin >> x >> s[i];
		son[x].push_back(i);//表示修x必须先修i,即x为i的父节点 
	}
	memset(dp, 0xcf, sizeof(dp));//0xcf即为-INF 
	DP(0);//从0节点开始,不需要先修任何课程 
	cout << dp[0][m] << endl;
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值