GDOI2007「夏娜的菠萝包」

题目

Description
问题描述:
夏娜很喜欢吃菠萝包,她的经纪人RC每半个月就要为她安排接下来的菠萝包计划。今天是7月份,RC又要去商场进货买菠萝包了。
这次RC总共买了N种菠萝包,每种一个。每个菠萝包都有一个初始美味值Ti,每过一天就会减少Di,即第2天美味值为Ti-Di,第3天为Ti-2*Di,依此类推。一旦美味值减为负数,那个包就坏掉了,不能吃了。
RC每天都要为夏娜安排当天吃菠萝包的组合,这些组合不是随意的,而是只能从夏娜喜欢的M种搭配中挑选一种。每种搭配是由Ki个菠萝包组成的,一种搭配的总美味值是这Ki个菠萝包当天的美味值之和再加上一个额外的搭配美味值Ei。不过要注意,一旦某种搭配的其中一个菠萝包坏掉了,这个搭配就不能选用了。而且,有可能存在两个搭配,里面的组合是一样的,但额外的搭配美味值却不同。
RC想让可爱的夏娜尽可能地吃得美味,因此希望能找出一种最优的方案,让小夏娜吃上若干天的菠萝包,这些天的美味值之和最大。
但RC面临着两个邪恶的敌人,一个叫bug,一个叫zzy,他们也想抢夺这个经纪人之位,因此要是他们提出更优的方案,RC就可能会失去他的夏娜了。那么,你们能帮帮这个可怜的RC吗?

Input
输入格式:
输入文件包含多组数据。
每组数据的第一行为一个正整数N(N<=14),表示菠萝包的种数,按1-N编号。
接下来N行,每行两个正整数Ti(Ti<100)和Di(Di<100),表示第i种菠萝包的初始美味值和每天递减值。
第N+2行为一个正整数M,表示搭配的种数。
接下来M(M<=20)行,每行先是一个正整数Ki,表示组成这个搭配的菠萝包数目,然后是一个非负整数Ei(Ei<100),表示这种搭配额外的美味值,最后是Ki个整数,每个整数为菠萝包的编号。
当N=0时表示输入结束。

Output
输出格式:
对于每组输入数据输出一行,仅包含一个整数,表示最大的美味值之和。

Sample Input

2
3 1
4 2
2
1 1 1
1 1 2
2
3 1
4 2
3
1 1 1
1 1 2
2 2 1 2
0

Sample Output

8
9

Hint
对于第一个样例,只有两个方案:
1、 第一天选择搭配1,即吃编号1的菠萝包,美味值为3+1=4;第二天选择搭配2,即吃编号为2的菠萝包,美味值为2+1=3。此时已把菠萝包都吃完了,总和为4+3=7.
2、 第一天选择搭配2,即吃编号为2的菠萝包,美味值为4+1=5;第二天选择搭配1,即吃编号1的菠萝包,美味值为2+1=3,此时已把菠萝包都吃完了,总和为5+3=8。
因此,第2个方案为最优方案,最大美味值总和为8.
对于第二个样例,除了上述两个方案,还有第三个:
3、 第一天选择搭配3,即编号为1和2的菠萝包一起吃,美味值为3+4+2=9。此时已经把菠萝包都吃完了,总和即为9.
虽然第3个方案只能吃1天,但因为其总和最大,所以选择第3个方案,答案为9。

思路

灼眼のシャナ!

但是这个RC是个什么??
跑题了\kk,毕竟现在的主角是菠萝包(大概)。

显然是一道DP题,再看看题目描述,发现需要压缩状态。
fi,kf_{i,k} 表示第 i 天,吃了状态为 k 的菠萝包的最大美味值。
容易想到状态转移方程 fi+1,S=max(fi,j,fi,S+ab).f_{i+1,S}=max(f_{i,j},f_{i,S}+a-b).
aa 为选择该方案可获取的贡献,即该方案内物品的美味值的总和与该方案的额外贡献;
bb 为直到第 i+1 天,该方案内物品所减少的贡献。

#include <stdio.h>
#include <string.h>
#include <algorithm>
#define reg register
using namespace std;
#define maxn 1<<14
int f[101][maxn];
int n, m;
int T[50], D[50], Time[50];
struct node {
	int State;
	int Value;
	int Time;
	int Cut;
} C[50];

int main() {
	while (~scanf("%d", &n)) {
		if (!n) break;
		memset(C, 0, sizeof C);
		memset(f, 0, sizeof f);
		
		for (reg int i=1; i<=n; i++) {
			scanf("%d %d", &T[i], &D[i]);
			Time[i]=T[i]/D[i];
		}
		
		scanf("%d", &m);
		for (reg int i=1, K; i<=m; i++) {
			C[i].Time=1<<30;
			scanf("%d %d", &K, &C[i].Value);
			for (reg int j=1, num; j<=K; j++) {
				scanf("%d", &num);
				C[i].Value+=T[num];
				C[i].Cut+=D[num];
				C[i].Time=min(C[i].Time, Time[num]);
				C[i].State=(C[i].State+(1<<(num-1)));
			}
		}
		
		int ans=0;
		for (reg int k=0; k<(1<<n); k++) {
			for (reg int i=1; i<=m; i++) {
				if (C[i].State&k) continue;
				for (reg int j=C[i].Time; j>=0; j--) {
					f[j+1][k+C[i].State]=max(f[j+1][k+C[i].State], f[j][k]+C[i].Value-(j*C[i].Cut));
					ans=max(ans, f[j+1][k+C[i].State]);
				}
			}
		}
		printf("%d\n", ans);
	}
	
	return 0;
}
posted @ 2020-08-10 21:55  willbe233  阅读(81)  评论(0)    收藏  举报