一、问题简析
本题是一道 01背包
。重点是 \(K\) 的限制,使我们在取 \(a_i\) 时,不能无所顾忌地套用模板 \(dp_{i-1,j-a_i.worth}+a_i.worth\) 。我们必须找到上一张合法的 \(a_j\),即 \(a_j\) 与 \(a_i\) 之间的日期不小于 \(K\)。
通过以下步骤确定 \(a_j\):
- 将每张支票的日期转换为天数,并按升序排序
- 用一个
last[]
数组记录 \(a_i\) 上一张合法的支票的编号 - 此时,方程变为 \(dp_{i,j}=max(dp_{i-1,j},dp_{last_i,j-a_i.worth}+a_i.worth)\)
二、Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll quickin(void)
{
ll ret = 0;
bool flag = false;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-') flag = true;
ch = getchar();
}
while (ch >= '0' && ch <= '9' && ch != EOF)
{
ret = ret * 10 + ch - '0';
ch = getchar();
}
if (flag) ret = -ret;
return ret;
}
typedef pair<int, int> P; // first -- 日期转换为天数; second -- 价值
const int MAX = 1e3 + 5;
int N, M, K, last[MAX];
P A[MAX];
int days[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int psum[15], dp[5010][5010];
bool cmp(const P &a, const P &b)
{
return a.first < b.first;
}
int main()
{
#ifdef LOCAL
freopen("test.in", "r", stdin);
#endif
psum[0] = 0;
for (int i = 1; i <= 12; ++i)
psum[i] = psum[i - 1] + days[i]; // psum[i] -- 1月到i月有几天
N = quickin(), M = quickin(), K = quickin();
for (int i = 1; i <= N; ++i)
{
int a, b, c;
a = quickin(), b = quickin(), c = quickin();
A[i].first = psum[a - 1] + b; // 将日期转换为为天数
A[i].second = c; // 记录票据价值
}
sort(A + 1, A + 1 + N, cmp); // 按天数升序排序
for (int i = 1; i <= N; ++i)
for (int j = i - 1; j >= 0; --j)
if (A[i].first - A[j].first >= K)
{
last[i] = j; // last[i] -- 第i张票据上一张合法的票据
break;
}
for (int i = 1; i <= N; ++i)
for (int j = M; j >= A[i].second; --j)
dp[i][j] = max(dp[i - 1][j], dp[last[i]][j - A[i].second] + A[i].second);
cout << dp[N][M] << endl;
return 0;
}
完
标签:ch,last,P8803,int,31,蓝桥,--,2022,dp From: https://www.cnblogs.com/hoyd/p/18193827