这道题目使用dp还要乘以100转化为0-1背包问题,使用搜索版本更容易上手一点,终于体会到减枝的魅力,在深度递归中如果只像毕业bg那道那样不取当前节点的展开不施加任何条件的话会超时(那道因为取的路径限制很严,而不像这里是一个固定值,那里可能也可以像这样优化吧,觉得没用,要根据具体数据关系特征选择减枝方案),而添加一个当前积累的money值加上除去当前结点之外的剩余值之和能否超过目前得到的最大报销额,没有的话这个节点就不再展开,直接回溯;添加这个条件的话耗时 0ms 天啦 天壤之别,其实也容易理解 :最大报销值的限制只适用于
该值较小的情况让其很早回溯,但当该值很大情况就收效甚微(极端情况是大于所有发票额的综合,整棵树遍历 一个测试用例可达2的30次方)这个限制然是针对取当前值而用的 我们还可以对不取当前值增加一个限制 就是去掉当前结点所剩额能超过目前得到的最大值,这样对 限额比较大时特别有用(原来以为从大到小排列性能会更高,经测试都一样) 两者共同使其达到平衡;
ps:浙大的题目老师设置一些细微的细节作为陷阱,以后要小心 认真读题目的条件,一般没有什么信息是多余的,开始忽视了相同编号的好几项之和不能超过600 几项分离并不是没有意义的,开始审题忽视了这个信息出问题修改很难发现,以后对此类信息要提高敏感度
#include<iostream>
#include<algorithm>
#include <iomanip>
using namespace std;
double price[32];
int top;
double maxget;
double maxcost;
void dfs(int i,int n,double nowcost,double remain)//maxcost不变作为全局变量
{
if(i>n)
{
if(nowcost>maxget)maxget=nowcost;return;
}
if(nowcost+remain-price[i]>maxget)//减枝的妙用,其实两个是串通的 尽早放弃不再往深处延伸
dfs(i+1,n,nowcost,remain-price[i]);//不计当前发票
if(nowcost+price[i]<=maxcost/*&&nowcost+remain>maxget*/)//觉得这个条件没用,是靠不取减枝,两头夹
dfs(i+1,n,nowcost+price[i],remain-price[i]);//包括当前发票
}
int cmp(double x,double y)
{
return x<y;
}
int main()
{
int n;
while(cin>>maxcost>>n&&n)
{
top=-1;
while(n--)
{
double cost=0;
double arr[3]={0};
int tag=1;
int m;
cin>>m;
char type,c;
double in;
while(m--)
{
cin>>type>>c>>in;
if(in>600||(type!='A'&&type!='B'&&type!='C'))tag=0;//don't break;
if(type=='A')arr[0]+=in;
if(type=='B')arr[1]+=in;
if(type=='C')arr[2]+=in;
cost+=in;
}
if(cost>1000)tag=0;
if(arr[0]>600||arr[1]>600||arr[2]>600)tag=0;
if(cost>maxcost)tag=0;//减枝,不在递归里意义不大
if(tag)price[++top]=cost;
}
sort(price,price+top+1,cmp);
maxget=0;
double remain=0;
for(int t=0;t<=top;t++)remain+=price[t];
dfs(0,top,0,remain);
cout<<setiosflags(ios::fixed)<<setprecision(2)<<maxget<<endl;
}
return 0;
}