比赛传送门
传送门
G-Gift
题面
思路
首先有一个坑,2021年的2月是没有29号滴;
归纳一下题意,我们对于每个朋友可以有三种选择;
- 不搭理他
- 给他做蛋糕
- 送他礼物
不难发现,礼物与做蛋糕是可以独立开来的;
我们首先考虑礼物;
因为礼物数量很少(至多15),我们可以考虑暴力枚举(选或不选)来完成,花费时间为 O ( 2 M ) O(2^{M}) O(2M),当然也可以用DP来解决;
接着假设只有做蛋糕,没有送礼物这一说;
f ( i , j ) 表 示 前 i 个 人 , 做 c a k e 不 超 过 j 天 得 到 的 最 大 价 值 f(i,j)表示前i个人,做cake不超过j天得到的最大价值 f(i,j)表示前i个人,做cake不超过j天得到的最大价值
这不就是一个经典的0-1背包吗?
接着我们引入礼物;
f ( i , j , k ) 表 示 前 i 个 人 , 做 c a k e 不 超 过 j 天 , 给 k 个 人 送 g i f t 所 得 到 的 最 大 价 值 f(i,j,k)表示前i个人,做cake不超过j天,给k个人送gift所得到的最大价值 f(i,j,k)表示前i个人,做cake不超过j天,给k个人送gift所得到的最大价值
状态方程也不难想到,就按照上面说的,忽略/送蛋糕/送礼物来转移即可;
f
[
i
]
[
j
]
[
k
]
=
f
[
i
−
1
]
[
j
]
[
k
]
,
忽
略
f[i][j][k] = f[i-1][j][k],忽略
f[i][j][k]=f[i−1][j][k],忽略
f
[
i
]
[
j
]
[
k
]
=
f
[
i
−
1
]
[
j
−
w
]
[
k
]
+
v
,
送
蛋
糕
f[i][j][k] = f[i-1][j-w][k]+v,送蛋糕
f[i][j][k]=f[i−1][j−w][k]+v,送蛋糕
f
[
i
]
[
j
]
[
k
]
=
f
[
i
−
1
]
[
j
]
[
k
−
1
]
)
,
送
礼
物
f[i][j][k] = f[i-1][j][k-1]),送礼物
f[i][j][k]=f[i−1][j][k−1]),送礼物
最后加一个常见的滚动数组优化一维度即可;
(不搞也行)
Code
#include <iostream>
#include <cstring>
#include <string>
#include <vector>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int months[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
int gmx[20];
struct Friend{
int dist_day,w,v;
bool operator<(const Friend& o) const{
return dist_day < o.dist_day;
}
};
int change(string date){
//yyyy-dd-mm
//5,6,8,9
int month = ((date[5] - '0') * 10) + (date[6] - '0');
int day = ((date[8] - '0') * 10) + (date[9] - '0');
int ret = day;
for(int i=1;i<month;++i) ret += months[i];
return ret;
}
int n,m,money,w[20],v[20];
void init_gifts(){
memset(gmx,0,sizeof gmx);
for(int i=1;i<=m;++i){
cin >> w[i] >> v[i];
}
for(int s = 1;s < (1 << m);++s){
//依次看看买了哪几个
int spend = 0,val = 0,cnt = 0;
for(int j=1;j<=m;++j){
if(s & (1 << (j-1))){
spend += w[j];
val += v[j];
++cnt;
}
}
if(spend <= money){
gmx[cnt] = max(gmx[cnt],val);
}
}
//买的多不一定好,取一个max前缀
for(int i=1;i<=m;++i)
gmx[i] = max(gmx[i],gmx[i-1]);
}
//f(i,j,k)表示前i个人,做cake不超过j天
//给k个人送gift所得到的最大价值
int f[2][370][20];
vector<Friend> friends;
int dp(){
memset(f,-0x3f,sizeof f);
for(int j=0;j<=365;++j)
f[0 & 1][j][0] = 0;
int ret = 0;
for(int i=1;i<=n;++i){
auto &ff = friends[i-1];
int birth = ff.dist_day,w = ff.w,v = ff.v;
for(int j=0;j<=365;++j){
for(int k=0;k<=m;++k){
//忽略
f[i & 1][j][k] = f[(i-1) & 1][j][k];
//做cake
if(j >= w && birth >= j)
f[i & 1][j][k] = max(f[i & 1][j][k],f[(i-1) & 1][j-w][k]+v);
//送gift
if(k > 0)
f[i & 1][j][k] = max(f[i & 1][j][k],f[(i-1) & 1][j][k-1]);
ret = max(ret,f[i & 1][j][k] + gmx[k]);
}
}
}
return ret;
}
void solve(){
friends.clear();
cin >> n >> m >> money;
int t = 0;
for(int i=1;i<=n;++i){
string date;
int w,v;//花费,价值
cin >> date >> w >> v;
if(date[5] == '0' && date[6] == '2' && date[8] == '2' && date[9] == '9'){
++t;
continue;
}
friends.push_back({change(date),w,v});
}
n -= t;
sort(friends.begin(),friends.end());
init_gifts();
cout << dp() << '\n';
}
signed main(){
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int t;
cin >> t;
while(t--)
solve();
return 0;
}