JZOJ 小奇采药 (深度优先搜索-dfs)
来源:JZOJ
题目描述
小奇是只天资聪颖的喵,他的梦想是成为世界上最伟大的医师。
为此,他想拜喵星球最有威望的医师为师。
医师为了判断他的资质,给他出了一个难题。
医师把他带到一个到处都是草药的山洞里对他说:
“小奇,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。
我会给你一段时间,在这段时间里,你可以采到一些草药。
如果你是一只聪明的喵,你应该可以让采到的草药的总价值最大。”
解题思路
- 这道题乍一看,还以为就是个背包的模板题,细细一看...不就是道模板题嘛!
- 信心满满地以
瞬雷不及掩耳之势打好了代码,提交!!! - \(30\) 分!\(QAQ\)
- 原来,这道题 要用 搜索!
- 哈哈!惊异到了吧!让我们来
正经地理一下思路: - 数据有点坑,所以,有请 \(dfs\) 中的玄学——剪枝 登场!
- 先按时间从大到小排序,求一个后缀和,\(sw[i]\) 表示 \(i\) 到 \(n\) 的时间和,\(sv[i]\) 表示 \(i\) 到 \(n\) 的价值和,然后就做一个背包的 \(dfs\) 吧!
剪枝技巧:
- First:如果当前的时间和加上 \(a[n].w\) 还超过了背包容量,也就是不管那哪一件往背包里塞都装不下了,就退出
- Second:如果当前的价值和加上 \(sv[x]\) 也就是说 \(x\) 后的价值和仍然小于最优值,就退出
- Third:如果当前的时间和加上 \(sw[x]\) 也就是说 \(x\) 后的时间和小于背包容量,就拿当前的价值和加上 \(sv[x]\) 和原最优解比较,如果当前的更优,更新,这时候就没有再递归下去的必要了,直接退出
- 注:也许思路有些别扭,静下心来,仔细思考一下,这三个剪枝是很有效的
Code
#include <bits/stdc++.h>
using namespace std;
long long ans=0;
long long sw[205],sv[205];
int n;
long long m;
struct node
{
int w,v;
}a[205];
inline int read() //快读
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
bool mycmp(node a,node b)
{
return a.w>b.w;
}
void dfs(int x,long long sumw,long long sumv)
{
ans=max(ans,sumv);
if (sumw+a[n].w>m) return; //三个剪枝,上文已给出
if (sumv+sv[x]<ans) return;
if (sumw+sw[x]<=m)
{
ans=max(ans,sumv+sv[x]);
return;
}
if (sumw+a[x].w<=m) dfs(x+1,sumw+a[x].w,sumv+a[x].v); //如果可以取当前这个,递归
dfs(x+1,sumw,sumv); //不取
}
int main()
{
freopen("herb.in","r",stdin);
freopen("herb.out","w",stdout);
int T;
T=read();
for (int t=1;t<=T;t++)
{
scanf("%d %lld",&n,&m);
for (int i=1;i<=n;i++) scanf("%d %d",&a[i].w,&a[i].v);
sort(a+1,a+n+1,mycmp); //按时间从大到小排序
sw[n+1]=0; sv[n+1]=0; //初值,因为是递推,所以没必要memset
for (int i=n;i>=1;i--) //后缀和
{
sw[i]=sw[i+1]+a[i].w;
sv[i]=sv[i+1]+a[i].v;
}
ans=0;
dfs(1,0,0);
printf("%lld\n",ans); //输出最优值
}
return 0;
}