/*
饮料供货:
大家对每一种饮料的满意度是知道的。微软供应部每天供应总量为V的饮料。每种饮料的单个容量都是2的方幂,比如王老吉,都是2^3 = 8升的,可乐都是
2^5 = 32升的。每种饮料也有购买量的上限。统计数据中用饮料名字,容量,数量,满意度描述每一种饮料。
如何求出保证最大满意度的购买量?
分析:
假设STC提供n种饮料,用(Si,Vi,Ci,Hi,Bi)(对应的是饮料的名字,容量,可能的最大数量,满意胡,实际购买量)
来表示第i种饮料(i = 0 ,1 ,..., n-1),其中可能的最大数量值STC存货的上限。
饮料总容量为:对Vi*Bi累积求和
总满意度为:对Hi*Bi累积求和
题目转化为在总容量为V的情况下,求总满意度最大的情况。这是最优问题。
看到最优,首先想到动态规划。用Opt(V',i)表示从第i,i+1,i+2,...,n-1(起始下标为0)种饮料中,
算出总量为V'的方案中满意度之和的最大值。
因此Opt(V,0)就是我们要求的值
如下推导公式:
Opt(V',i) = max{k*Hi + Opt(V' - Vi*k,i+1)}
(k = 0 ,1,,...,Ci,i = 0,1,..,n-1)
最优结果 = “选择k个第i种饮料的满意度 + 剩下部分不考虑第i种饮料的最优化结果”的最大值。
边界条件:
Opt(0,n) = 0
Opt(x,n) = -INF(x != 0)(-INF为负无穷大),即在容量不为0的情况下, 把最优化结果设为负无穷大,并把它作为
初值。
0-1背包:从后向前
0-1无限背包:从前向后
0-1多重背包:从前向后
输入:
总容积 所有饮料种类
输出:
最大满意度
解法2:
动态规划法的变形是备忘录法,通过使用表格保存已经解决的子问题的答案,通过记忆化搜索避免计算不可能状态
。具体实现:为每个子问题建立一个记录项,初始化,给记录项存入一个特殊值,表示该子问题尚未求解。
求解时,对每个待求解子问题,首先查看其对应的记录项,若是初始值,则表示该子问题第一次遇到,求解并
保存。若不是初始值,直接提取答案即可。
*/
/*
关键
*/
#include <stdio.h>
#include <string.h>
const int MAXSIZE = 1000;
int dp[MAXSIZE][MAXSIZE];
const int INT_MAX = 0x7fffffff;
const int INT_MIN = INT_MAX * -1;
typedef struct Beverage
{
char _sName[50];//饮料名
int _iVol;//容积
int _iMaxNum;//最大选取数量
int _iStatis;//满意度
int _iBuy;//实际购买数量
}Beverage;
int max(int a,int b)
{
return a > b ? a : b;
}
//函数流程:首先设定dp[0][T] = 0,dp[i][T] = -INF,从第T-1种饮料开始,向前遍历,对每种体积从0到V的
//情况设定opt[i][j] = -INF,对遍历到第某种饮料时,遍历其选取的瓶数,如果此时总体积<瓶数乘以*每瓶的
//容量,就退出,相当于这部分没有选取。余下的情况就是总体积>瓶数乘以数量时,逆推,满意度 =
//dp[i - k*V[j]][j+1],判断状态是否可得到,不可得,剪枝过滤,如果客的,则满意度要累加上
//瓶数乘以满意度,并且与原有dp[i][j]做比较,如果打,才进行更新,否则,不更新
int package(int V,int T)
{
dp[0][T] = 0;//总体积为0的情况下,从第T种,到第T-1种饮料中,算出的满意度最大值
for(int i = 1 ; i <= V ; i++ )//设定其他体积下,从第T种,到第T-1种饮料中,算出的满意度最大值为负无穷
{
dp[i][T] = INT_MIN;
}
for(int j = T-1 ; j >= 0 ; j--)
{
for(int i = 0 ; i <= V ; i++)
{
dp[i][j] = INT_MIN;
for(int k = 0 ; k <= C[j] ; k++)//(Si,Vi,Ci,Hi,Bi),遍历第j种饮料的选取数量k
{
if(i < k*V[j])//如果总体积,小于选取的第j种饮料的总体积,那么直接遍历下一个体积
{
break;
}
if(INT_MIN != dp[i-k*V[j]][j+1])//总体积减去第j个物体的体积,选择的是从j+1,...T-1物体总
//体积在i-k*V[j]时所能得到的最大满意度,如果该状态是可以得到的
{
dp[i][j] = max(dp[i][j],dp[i-k*V[j]][j+1] + k*H[j]);
}
}
}
}
return dp[V][0];//空间复杂度为V*T,dp[V][T],时间复杂度为O(V*N*max(Ci))
}
//子问题的记录项表,假设从i到T种饮料中,找出容量综合为V'的一个方案,满意度最多能够达到opt(V',i,T-1),
//存储于opt[V'][i]。初始化时opt中存储值为-1,表示该子问题尚未求解。
int _opt[MAXSIZE][MAXSIZE];
int T;
int cal(int V,int type)//type表示第几种饮料
{
if(type == T)
{
if(V == 0)
{
return 0;
}
else
{
return INT_MIN;
}
}
if(V < 0)
{
return INT_MIN;//这是不可能的
}
else if(V == 0)
{
return 0;//因为opt[0][j] = -INF
}
else if(_opt[V][type] != -1)//如果已经求解了子问题,就不再求解
{
return _opt[V][type];
}
int ret = INT_MIN;
for(int i = 0; i <= C[type] ; i++)//选取的饮料数量
{
int iTemp = cal(V - i*V[type],type+1);//递归求解
if(iTemp != INT_MIN)
{
iTemp += i*H[type];
if(iTemp > ret)
{
ret = iTemp;
}
}
}
return _opt[V][type] = ret;
}
void process()
{
memset(_opt,-1,sizeof(_opt));
}
int main(int argc,char* argv[])
{
process();
getchar();
return 0;
}