https://ac.nowcoder.com/acm/contest/12548/G
题意:
假设现在是\(2021\)年的第一天。你有\(n\)个朋友,\(m\)个可购买的礼物,以及\(w\)元。给出每个朋友的生日、你亲手为他们制作礼物的时间以及你亲手制作礼物能得到的好感度。你每天只能进行一种礼物的制作,但可以不连续制作同一个礼物。当然,你也可以选择直接购买礼物,花费\(a_i\)元,对于每个朋友,都能获得\(b_i\)好感度。每个人都只能在生日当天收一次礼物,问你今年能收获的最大好感度是多少。
思路:
想不到好的贪心,题面也有点像背包,那么考虑\(dp\)。考虑他给出的各项参数,发现\(O(365 * N * M * T)\)差不多正好能符合\(1s\)的时限。生日是有先有后的,我们要符合递推的顺序。所以我们记录下每个人生日距离\(1\)月\(1\)日的天数,以天数为关键字进行升序排序,同时第一维也可以降下来。\(dp[i][j]\)代表花费i天制作礼物,买了\(j\)个礼物后,能获得的最大好感度。写的时候发现买礼物时,并不好决定当下买哪个礼物,那么我们预处理一下,用状压处理出买对应数量礼物时,能获得的最大好感度,在最后找答案的时候加上去就行了。注意背包的两维要从后往前,避免重复送礼物。最后还要注意\(2\)月\(29\)的情况,因为\(2021\)年不是闰年,要直接不管这一天。一直想知道这天过生日的是4年过一次吗(逃
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int inf = 0x3f3f3f3f; const ll INF = 0x3f3f3f3f3f3f3f3f; const int N = 5e2 + 7; struct Fir{ int d, c, v; Fir () {} Fir (int d, int c, int v) : d(d), c(c), v(v) {} bool operator < (const Fir & f) const { return d < f.d; } }fir[N]; int n, m, w; int a[17], b[17]; int dp[371][17]; int add[17]; int pre[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; void solve() { memset(dp, -0x3f, sizeof(dp)); memset(add, 0, sizeof(add)); scanf("%d %d %d", &n, &m, &w); int tot = 0; for (int i = 1; i <= n; ++i) { int yy, mm, dd, cc, vv; scanf("%d-%d-%d %d %d", &yy, &mm, &dd, &cc, &vv); if (mm == 2 && dd == 29) continue; tot++; fir[tot] = Fir(pre[mm - 1] + dd, cc, vv); } sort(fir + 1, fir + 1 + tot); for (int i = 1; i <= m; ++i) { scanf("%d %d", &a[i], &b[i]); } int ed = 1 << m; for (int i = 1; i < ed; ++i) { int cost = 0; int val = 0; int num = 0; int sta = i; for (int j = 1; j <= m; ++j) { if (sta & 1) cost += a[j], val += b[j], num++; sta >>= 1; } if (cost <= w) add[num] = max(add[num], val); } dp[0][0] = 0; for (int i = 1; i <= tot; ++i) { for (int j = 365; j >= 0; --j) { for (int k = m; k >= 0; --k) { if (k + 1 <= m) dp[j][k + 1] = max(dp[j][k + 1], dp[j][k]); if (j + fir[i].c <= 365 && j + fir[i].c <= fir[i].d) { // 这里注意判断下之前的天数够不够,不够的话是不能赶在生日当天送礼物的 dp[j + fir[i].c][k] = max(dp[j + fir[i].c][k], dp[j][k] + fir[i].v); } } } } int ans = -inf; for (int i = 1; i <= 365; ++i) { for (int j = 0; j <= m; ++j) { ans = max(ans, dp[i][j] + add[j]); } } printf("%d\n", ans); } int main() { for (int i = 1; i <= 12; ++i) pre[i] += pre[i - 1]; int t = 1; scanf("%d", &t); while (t--) solve(); return 0; }