1040. 【GDOI2007】夏娜的菠萝包【推荐】

64 篇文章 0 订阅
10 篇文章 0 订阅

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

Data Constraint

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。

Solution

状压dp。设f[ i ][ S ]表示第 i 天,吃了状态为S的菠萝包的最大美味值。

f[ 0 ][ 0 ]=0

转移枚举每种搭配以及菠萝包的种类,将每个包的美味度算出来加到f[ i +1 ][ T ]里面。

Code

#include<cstdio> 
#include<cstring>
#include<algorithm>
#include<cmath>
#define I int
#define ll long long
#define F(i,a,b) for(I i=a;i<=b;i++)
#define Fd(i,a,b) for(I i=a;i>=b;i--)
#define N 22
using namespace std;
I n,m,T,x,a[N][N],t[N],d[N],k[N],st[N],mt[N],e[N],f[N][20000],now,bz,tot,ans,inf;
char c;
void R(I &x){
	x=0;c=getchar();
	while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
}
I main(){
	while(1){
		scanf("%d",&n);
		if(!n) return 0;
		memset(f,-127,sizeof f);inf=f[0][0];
		f[0][0]=ans=0;
		F(i,1,n) scanf("%d%d",&t[i],&d[i]);
		scanf("%d",&m);
		F(i,1,m){
			st[i]=mt[i]=0;
			scanf("%d%d",&k[i],&e[i]);
			F(j,1,k[i]){
				scanf("%d",&a[i][j]);
				st[i]|=(1<<a[i][j]-1);
				mt[i]=max(mt[i],t[a[i][j]]/d[a[i][j]]+1);
			}
		}
		F(i,0,min(n,m)-1){
			F(S,0,(1<<n)-1) if(f[i][S]>=0){
				F(l,1,m){
					if((st[l]&S)||mt[l]<i+1) continue;
					T=now=0;
					F(j,1,k[l]){
						T|=(1<<a[l][j]-1);
						now+=t[a[l][j]]-d[a[l][j]]*i;
					}
					f[i+1][S+T]=max(f[i+1][S+T],f[i][S]+now+e[l]);
					ans=max(ans,f[i+1][S+T]);
				}
			}
		}
		printf("%d\n",ans);
	}
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值