hrbust 1663 水果【状压dp】【原创1000篇纪念!!!】

来源:互联网 发布:安居客网络门店管理 编辑:程序博客网 时间:2024/06/16 02:13

水果Time Limit: 3000 MSMemory Limit: 32768 KTotal Submit: 20(6 users)Total Accepted: 9(6 users)Rating: Special Judge: NoDescription

吃水果可以补充维生素ABCDE….

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 Input2
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 Output8
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);    }}






0 0
原创粉丝点击