前言
看到这个题目的第一眼就觉得应该用回溯法,思路就是从S1开始放,递归结束后再放S2,递归结束的条件是当背包的剩余空间小于min(min=S1<S2?S1:S2)时,判断这条解答树路径上的价值之和是不是比全局变量ans大,大则赋值给ans,否则结束递归。但是这会有一个问题,当背包的容量大而S1和S2特别小时,如背包tot容量位100,S1为1,S2为2时会要进行很多次递归,浪费了很多的时间和空间资源,那么有什么办法解决呢?
一、什么情况下算是背包容量大而S1,S2小?
当背包容量tot>S1S2时,在这种情况下方便下一步计算,因为当tot>S1S2时,可以计算出tot最多大于多少个S1S2,假设用循环得出n个满足条件,则在这nS1S2的容量里,既可以装S1个V1也可以装S2个V2,而nS1S2=nS1S2,则假设当V1>V2时,nS1S2V1>nS1S2V2,那么接下来可以用回溯法计算在背包容量为(tot-nS1*S2)时的最大价值,此时不会因为前言里的问题而导致时间空间浪费。
二、使用步骤
1.判断是否tot>S1*S2,如果是则求出n
这一步可用for循环求出,代码如下(示例):
if (s[0] * s[1] < tot)
for (n = 1; n * s[0] * s[1] < tot; n++);
//或者去掉if,n从0开始循环
2.在背包容量为(tot-nS1S2)的条件下回溯
整体代码如下(示例):
int tot, s[2], v[2], ans = -1, mins;
void dfs(int rest, int sum)
{
if (rest < mins)
{
if (sum > ans)
ans = sum;
return;
}
for (int i = 0; i < 2; i++)
if (rest >= s[i])
int main()
{
int n = 0, ans1;
scanf_s("%d", &tot);
for (int i = 0; i < 2; i++)
scanf_s("%d %d", &s[i], &v[i]);
mins = s[0] < s[1] ? s[0] : s[1];
if (s[0] * s[1] < tot)
for (n = 1; n * s[0] * s[1] < tot; n++);
tot -= n * s[0] * s[1];
ans1 = n * (v[0] > v[1] ? v[0] : v[1]);
dfs(tot, 0);
printf("%d", ans + ans1);
return 0;
}
总结
提示:因为网络原因没有在Uva上进行评测,只是在洛谷上试了一下样例并通过了,觉得这个想法很妙就先记录下来。