一开始不知道这种方法,但当时大致思路和这个很像,自己做的时候只有25。第一次的方法问题在于:
比如说10 20 30 40 50 60 70,我可以算得10+20+30,算不得10+20+40,即我不能选择在买了两本以上的书之后,其它的书如果买会怎样,如果不买会怎样,少考虑了很多种可能。
第二次查阅资料,学会下面这种做法,25->70☑️
思路就是求出所有可能,遇到满足条件的就检查是否需要更新结果。dfs的核心就是,如果选这本书,则检查是否不小于x,是,则和已有的最小值比较,否,则接下去考虑是否选下一本书。如果不选这本书,接下去考虑是否选下一本书。
#include<iostream>
#include<algorithm>
using namespace std;
int m=1000010;
void dfs(int nownum,int s,int a[],int n,int x){
if(m==x){return;}
for(int i=nownum;i<n;++i){
if(s+a[i]>=x){
m=min(s+a[i],m);
}
else{
dfs(i+1,s+a[i],a,n,x);
}
}
return;
}
int main(){
int n,x;
cin>>n>>x;
int a[n];
for(int i=0;i<n;++i){
cin>>a[i];
}
dfs(0,0,a,n,x);
cout<<m;
}
但是有没有可能,即使选了剩下全部的书也不能达到x?这个时候就不需要考虑了。基于这个,我们可以对现有dfs算法进行修改。(因为时隔几天才重做的,对dfs理解得更透彻一点,所以这次dfs表达方式也不一样了)
剩下的书的总价可以用前缀和计算,之前有详细介绍前缀和,这里要求的相当于是一个大线段减去小线段,剩下的书的总价即为剩下的线段。
老规矩,如果已经到边界,就结束函数;如果剩下的所有书都买还凑不够包邮,也结束函数;剩下两种情况和第一次写的dfs一样。
#include<iostream>
#include<algorithm>
using namespace std;
int book[31];
int sum[32]={0};
int minmoney=300010;
void dfs(int now,int s,int n,int x){
if(now==n){return;}
if(s+sum[n]-sum[now]<x){return;}
if(s+book[now]>=x){
minmoney=min(minmoney,book[now]+s);
}
else{dfs(now+1,s+book[now],n,x);}
dfs(now+1,s,n,x);
}
int main(){
int n,x;
cin>>n>>x;
for(int i=0;i<n;++i){
cin>>book[i];
sum[i+1]=sum[i]+book[i];
}
dfs(0,0,n,x);
cout<<minmoney;
}
第三次再查阅资料,看到了这种方法。
同样是记录下所有可能,判断某个总价值是否有组合能实现,如果有,则该总价值是存在的。本质上呢还是背包问题,水平地走遍一个数的所有可能。如果说dfs是纵深的,它就是水平方向的。
利用数组下标代表总价值,最后从下标为x(包邮线)开始遍历,第一个价值存在的下标则为最低价格。
#include<iostream>
using namespace std;
int s[300010]={0};
int main(){
int n,x;
cin>>n>>x;
s[0]=1;
for(int i=0;i<n;++i){
int tempnum;
cin>>tempnum;
for(int j=300009;j>=tempnum;--j){
if(j-tempnum>=0 and s[j-tempnum]==1){
s[j]=1;
}
}
}
for(int i=x;i<=300009;++i){
if(s[i]==1){
cout<<i;
break;
}
}
}