第十三届蓝桥杯 b 组国赛 d p 问题 \Huge{第十三届蓝桥杯b组国赛dp问题} 第十三届蓝桥杯b组国赛dp问题
刷题的时候发现往年国赛题中有三道dp问题,而且还都是背包问题,正好最近没写过dp,那就简单整理一下,尽量把我思路整理清楚hhh。
关于背包问题,可以查看这篇博客:背包九讲——九种背包问题的算法思路+代码分析-CSDN博客
题目链接:备赛蓝桥杯 - 蓝桥云课 (lanqiao.cn)
2022由10个不同的正整数相加得到的方案个数。
我们可以将本问题看作是01背包,那么此时问题可以改为:有编号为 1 , 2 , 3 , . . . , 2022 1,2,3,...,2022 1,2,3,...,2022的2022个物品,每个物品的体积和价值都等于它的编号,因此物品的体积和价值可以看作一个限制条件,然后需要选10个数,作为第二个限制条件。
那么本题的限制条件就有两个,然后呢?
那不就是一道二维费用流背包吗?
众所周知,二维费用流背包就是在01背包的基础上加了一个或多个限制条件,那我们只需将加一层枚举,来枚举体积和个数即可。
void Solved() {//在二维01背包的基础上增加一维,用来枚举所选个数
long long f[2023][11][2023];
for(int i = 0; i <= 2022; i ++ ) f[i][0][0] = 1;
for(int i = 1; i <= 2022; i ++ )
for(int j = 1; j <= 10; j ++ )
for(int k = 1; k <= 2022; k ++ ) {
f[i][j][k] = f[i - 1][j][k];
if(k >= i) {
f[i][j][k] += f[i - 1][j - 1][k - i];
}
}
cout << f[2022][10][2022] << endl;
}
void Solved() {//优化一维,详细优化思路可以查看上面背包问题的博客
long long f[11][2023];
f[0][0] = 1;
for(int i = 1; i <= 2022; i ++ )
for(int j = 10; j >= 1; j -- )
for(int k = i; k <= 2022; k ++ ) {
f[j][k] += f[j - 1][k - i];
}
cout << f[10][2022] << endl;
}
给出n个砖,每块砖对应的有体积和价值。现要求将砖块摞起来,要求每块砖上面的所有砖加起来的重量不超过其自身的价值。求摞起来的最大总价值。
一道背包问题,但是增加了一些限制条件。
我们考虑:每块砖放与不放的状态是由其上面的砖块重量之和决定,那么我们需要从顶部考虑放砖块,才能保证不影响接下来的结果。
跟据贪心的思路:放在顶部的砖块需要是重量较小,且价值较大的砖块。
那么在枚举的过程中,我们需要按“重量较小,且价值较大”的顺序来优先考虑砖块,因此需要排序。
若有编号为1,2的两块石头,那么其重量和价值关系共有两种情况:
对于第一种情况,毫无疑问1应该放在2上面,对于第二种情况,只能1放在2上面。
因此排序规则为: w i + v i < w j + v j w_i+v_i < w_j+v_j wi+vi<wj+vj。
int n, f[200001], sum, res;
PII a[1005];
void Solved() {
cin >> n;
for(int i = 1; i <= n; i ++ ) {
cin >> a[i].fi >> a[i].se;
sum += a[i].fi;
}
sort(a + 1, a + n + 1, [](PII n1, PII n2) {
return n1.fi + n1.se < n2.fi + n2.se;
});
for(int i = 1; i <= n; i ++ ) {
for(int j = sum; j >= a[i].fi; j -- ) {
if(j - a[i].fi <= a[i].se) {
f[j] = max(f[j], f[j - a[i].fi] + a[i].se);
}
res = max(res, f[j]);
}
}
cout << res << endl;
}
给出若干张发票,每个发票有日期(月日)、价值。要求选出若干张发票,且相互日期间隔不小于k,总和不超过m,求能选出的最大总和。
本题有两个限制条件:
那么这道题就比较简单了,我们首先记录每一天最大的发票价值(当天没有发票就为0,有发票就为最大的数目)。
注意:可能会有某天有多张发票,而题目中k大于等于1,因此不可能选择同一天的发票。
然后枚举每一天,并判断是否需要加上当前天的发票。
#define int long long
int today(int mm, int dd) {
int mon[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31 ,30, 31, 30, 31};
int sum = dd;
for(int i = 1; i < mm; i ++ ) sum += mon[i];
return sum;
}
void Solved() {
int n, m, k, da[370] = {0};
cin >> n >> m >> k;
for(int i = 1; i <= n; i ++ ) {
int mm, dd, vv; cin >> mm >> dd >> vv;
int data = today(mm, dd);
da[data] = max(da[data], vv);
}
for(int i = 1; i <= 365; i ++ ) {
int t = max(0ll, i - k);
if(da[i] + da[t] < m) {
da[i] = max(da[i - 1], da[i] + da[t]);
} else {
da[i] = da[i - 1];
}
}
cout << da[365] << endl;
}