0
点赞
收藏
分享

微信扫一扫

45届ICPC昆明刷题

天蓝Sea 2022-03-11 阅读 29

比赛传送门

传送门

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)icakej

这不就是一个经典的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)icakejkgift

状态方程也不难想到,就按照上面说的,忽略/送蛋糕/送礼物来转移即可;

f [ i ] [ j ] [ k ] = f [ i − 1 ] [ j ] [ k ] , 忽 略 f[i][j][k] = f[i-1][j][k],忽略 f[i][j][k]=f[i1][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[i1][jw][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[i1][j][k1])

最后加一个常见的滚动数组优化一维度即可;

(不搞也行)

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;
}
举报

相关推荐

0 条评论