水果 | ||||||
| ||||||
Description | ||||||
吃水果可以补充维生素A、B、C、D、E…. 小z在月初买了N种水果,每种水果各买了一个。每种水果都有一个初始的新鲜度Ti,每过一天就会减少Di,即第二天的新鲜度为Ti-Di,第3天为Ti-Di*2,依次类推。一旦新鲜度变为负数,那个水果就不能吃了。 小z每天都按照一种水果的搭配来吃水果,这些搭配不是随意的,而是只能从事先计划好的M中搭配中选择一种。每种搭配由Ki个水果组成,一种搭配的营养值是由Ki个水果当天的新鲜度之和再加上一个额外的搭配营养值Ei组成。需要注意的是当某种搭配中的一个水果不能吃的时候,这个搭配就不能用了;而且,有可能存在两个搭配里面的组合是一样的,但是额外的搭配营养值不同。 为了能够获得更多的营养,小z希望找出一种最优的方案,你能帮助他计算出最优方案下一共获得的营养值么?
| ||||||
Input | ||||||
多组测试数据。 每组数据第一行输入一个整数N表示水果的种数,按1~N编号。(0<N<15) 接下来的N行,每行两个整数Ti,(Ti<100)和Di(Di<100),表示第i种水果的初始新鲜度和每天的递减值。 第N+2行为一个正整数M,表示搭配的种数。(0<M<20) 接下来的M行,每行先是一个正整数Ki,表示组成这个搭配的水果的数目,然后是一个非负整数Ei(Ei<100),表示这种搭配额外的营养值,最后是Ki个整数,每个整数为水果的编号。 | ||||||
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 | ||||||
Sample Output | ||||||
8 9 | ||||||
Author | ||||||
`Wind @hrbust |
思路:
1、观察到数据范围N,M都在20以内,那么考虑状压dp,其中又包含一个第几天到底吃第几种搭配的问题,那么我们设定dp【i】【j】表示第i天,吃了状态为j的水果的时候的最多营养值收获。(例如当前j=5,5=101(二进制),其表示我们现在吃了第一种和第三种食物,第二种食物并没有吃)
2、对应我们首先进行各项预处理:
①设定tmp【i】,表示第i种搭配的食物需要吃的水果的状态。
②设定dead【i】,表示第i天坏掉了的水果的状态。
3、然后我们考虑其状态转移方程:
①dp【i】【j】=max(dp【i】【j】,dp【i-1】【q】+val【k】+cal(i,k));
表示今天的状态我们从昨天转移过来。
②其中k表示第i天吃的搭配编号,val【k】表示第k种搭配获得的营养值,cal(i,k)表示所有食物新鲜值的总和。
③那么我们第一层for枚举i,第二层for枚举状态j,第三层for枚举k;表示我们第i天要吃第k种搭配的食物,那么我们接下来需要判断当前状态j是否可行。
④那么我们对应判断这一天的时候,第k种搭配中的食物有没有坏掉的,如果有坏掉的,那么不进行状态转移,接下来判断一下状态j是否包含第k种搭配中的所有食物。如果满足,那么当前dp【i】【j】就是一个可行状态。那么对应q=j-tmp【k】,表示上一天我没有吃第k种搭配的食物,那么我们接下来直接进行状态转移即可。
4、那么其解就在dp【i】【j】(0<=i<m&&0<=j<(1<<n))中,我们这里暴力维护一波最大值即可。
总时间复杂度估计为:O(N*M*(1<<N));
Ac代码:
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
struct node
{
int ti,di;
}a[50];
int n,m;
int dp[24][(1<<16)];
int dead[50];
int val[50];
int tmp[50];
int cal(int day,int k)
{
int sum=0;
for(int i=0;i<n;i++)
{
if(((1<<i)&tmp[k])!=0)
{
sum+=a[i].ti-a[i].di*day;
}
}
return sum;
}
int main()
{
while(~scanf("%d",&n))
{
memset(dead,0,sizeof(dead));
memset(dp,-1,sizeof(dp));
memset(val,0,sizeof(val));
memset(tmp,0,sizeof(tmp));
for(int i=0;i<n;i++)
{
scanf("%d%d",&a[i].ti,&a[i].di);
}
scanf("%d",&m);
for(int i=0;i<m;i++)
{
int kk;
scanf("%d%d",&kk,&val[i]);
while(kk--)
{
int x;
scanf("%d",&x);
x--;
tmp[i]+=(1<<x);
}
}
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(a[j].ti-a[j].di*i<0)
{
dead[i]+=(1<<j);
}
}
}
for(int i=0;i<m;i++)
{
int j=tmp[i];
dp[0][j]=max(val[i]+cal(0,i),dp[0][j]);
}
for(int i=1;i<m;i++)//第i天
{
for(int j=0;j<(1<<n);j++)//这一天的状态
{
for(int k=0;k<m;k++)//这一天想要吃第k种搭配的食物
{
if((j&tmp[k])==tmp[k])
{
if((tmp[k]&dead[i])==0)//这一天想要吃的食物中没有坏的
{
int q=j-tmp[k];
if(dp[i-1][q]==-1)continue;
dp[i][j]=max(dp[i-1][q]+val[k]+cal(i,k),dp[i][j]);
}
}
}
}
}
int output=0;
for(int i=0;i<m;i++)
{
for(int j=0;j<(1<<n);j++)
{
output=max(output,dp[i][j]);
}
}
printf("%d\n",output);
}
}