双向DFS + 二分、满足限制条件下的最大化以及最小化问题
双向DFS
会给出某种物品数量大概在30~40之间,询问挑选出来的物品价值或体积,他们的和满足一定的限制,需要你求出最佳价值(不小于某一个数里最小的、不大于某一个数中最大的)。
Q1
题目链接:https://www.acwing.com/problem/content/4703/
C++代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 35;
int n, m, k;
int w[N];
int nums[1 << 16], cnt;
int res = 1e6;
void dfs_1(int u, int s)
{
if(u == k)
{
nums[cnt ++] = s;
return ;
}
dfs_1(u + 1, s + w[u]);
dfs_1(u + 1, s);
}
void dfs_2(int u, int s)
{
if(u >= n)
{
int l = 0, r = cnt - 1;
while(l < r)
{
int mid = l + r >> 1;
if(nums[mid] + s >= m) r = mid;
else l = mid + 1;
}
if(nums[r] + s >= m) res = min(res, nums[r] + s);
return ;
}
dfs_2(u + 1, s + w[u]);
dfs_2(u + 1, s);
}
int main()
{
cin >> n >> m;
for(int i = 0; i < n; i ++) cin >> w[i];
sort(w, w + n);
reverse(w, w + n);
k = n / 2 + 1;
dfs_1(0, 0);
sort(nums, nums + cnt);
cnt = unique(nums, nums + cnt) - nums;
dfs_2(k, 0);
cout << res << endl;
return 0;
}
Q2
原题链接:https://www.acwing.com/problem/content/173/
C++代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 50;
int n, m, k;
int g[N];
int w[1 << 24], cnt;
int res;
void dfs_1(int u, int s)
{
if(u >= k)
{
w[cnt ++] = s;
return;
}
if(g[u] + 0ll + s <= m) dfs_1(u + 1, g[u] + s);
dfs_1(u + 1, s);
}
void dfs_2(int u, int s)
{
if(u >= n)
{
int l = 0, r = cnt - 1;
while(l < r)
{
int mid = l + r + 1 >> 1;
if(w[mid] + 0ll + s <= m) l = mid;
else r = mid - 1;
}
if(w[r] + s <= m) res = max(res, w[r] + s);
return;
}
if(s + 0ll + g[u] <= m) dfs_2(u + 1, s + g[u]);
dfs_2(u + 1, s);
}
int main()
{
scanf("%d%d", &m, &n);
long long s = 0;
for(int i = 0; i < n; i ++)
{
scanf("%d", &g[i]);
s += g[i];
}
if(s <= m)
{
printf("%d\n", s);
return 0;
}
sort(g, g + n);
reverse(g, g + n);
k = n / 2 + 1;
dfs_1(0, 0);
sort(w, w + cnt);
cnt = unique(w, w + cnt) - w;
dfs_2(k, 0);
printf("%d\n", res);
return 0;
}
注:Q1还可以使用DP来写,Q2只能使用双向DFS + 二分来写。