#P2293. 第3题-评估最大工作量
-
3000ms
Tried: 1013
Accepted: 122
Difficulty: 6
所属公司 :
华为
时间 :2024年9月25日-国内
-
算法标签>DFS
第3题-评估最大工作量
题面解释:
小塔的团队面临一个大项目,包含n个需求,每个需求需要不同的人天工作量。团队的工作量预算为T人天,目标是选择一些需求,使得在不超过T人天的前提下,所选需求的工作量总和最大。每个需求要么全部完成,要么不做。输入包含两行,第一行是两个整数n和T,第二行是n个整数,表示每个需求的工作量。输出一个整数,表示在预算内可以完成的最大工作量。
题解
该题可以使用状态压缩动态规划(DP)或深度优先搜索(DFS)结合二分查找来解决。由于背包容量可达到 10^9,而物品数量 n 为 40,使用普通的 01 背包算法显然不可行,因为 2^40 的复杂度会爆炸。因此,我们可以将物品分成两个组,每组最多有 20 个物品,这样总的复杂度将减少到 2^20,大约在 100 万级别,仍然可以接受。
具体思路如下:
1.对于每个组,使用状态压缩 DP 或 DFS 计算出所有可能的组合(重量和价值)。 2.枚举第一个组的每个组合 x,然后利用二分查找找到第二组中最接近 T - x 的组合 y。 3.更新答案为 ans = max(ans, x + y)。 整体时间复杂度为 O((2^(n/2)) log(2^(n/2))),这样就能高效地解决问题。 这样做的好处是大幅降低了复杂度,同时利用二分查找加速了查找过程,使得计算更为高效。
-
分组与组合生成:
- 将需求分成两组,分别为
a和b。 - 使用深度优先搜索(DFS)生成每组的所有组合的工作量。
- 将需求分成两组,分别为
-
组合搜索:
- 遍历第一组的每个组合
x,然后在第二组中利用二分查找找到不超过T - x的最大组合y。 - 更新答案为
ans = max(ans, x + y)。
- 遍历第一组的每个组合
-
效率优化:
- 通过对第二组的组合数组进行排序,并使用
upper_bound进行快速查找,可以显著提高查找效率。
- 通过对第二组的组合数组进行排序,并使用
整体时间复杂度为O((2n/2)log(2n/2)),使得计算过程更加高效。
解释
- 数据输入:程序首先读取需求数量和预算,然后分别读取两组需求的工作量。
- DFS组合生成:通过递归的方式生成每组需求的所有组合并存储。
- 组合查找:利用二分查找寻找在预算内的最大组合,最终输出满足条件的最大工作量。
代码如下
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, t; // n为需求数量,t为工作量预算
int a[25], b[25]; // 分别存储两组需求的工作量
int x1, x2; // x1和x2为两组需求的数量
vector<int> v1, v2; // 用于存储组合的工作量
int cnt1 = 0, cnt2 = 0; // 记录组合数量
// 深度优先搜索(DFS)生成第一组的组合
void dfs1(int x, int sum) {
if (x == x1 + 1) { // 当所有物品都考虑完毕
v1.push_back(sum); // 将当前组合的工作量加入结果集
return;
}
dfs1(x + 1, sum + a[x]); // 选择当前物品
dfs1(x + 1, sum); // 不选择当前物品
}
// 深度优先搜索(DFS)生成第二组的组合
void dfs2(int x, int sum) {
if (x == x2 + 1) { // 当所有物品都考虑完毕
v2.push_back(sum); // 将当前组合的工作量加入结果集
return;
}
dfs2(x + 1, sum + b[x]); // 选择当前物品
dfs2(x + 1, sum); // 不选择当前物品
}
signed main() {
cin >> n >> t; // 输入需求数量和预算
x1 = n / 2; // 第一组物品数量
x2 = (n + 1) / 2; // 第二组物品数量
for (int i = 1; i <= x1; i++) {
cin >> a[i]; // 输入第一组的工作量
}
for (int i = 1; i <= x2; i++) {
cin >> b[i]; // 输入第二组的工作量
}
// 生成两组的所有组合
dfs1(1, 0);
dfs2(1, 0);
// 对组合数组进行排序
sort(v1.begin(), v1.end());
sort(v2.begin(), v2.end());
v2.push_back(1e18); // 添加一个极大值以方便查找
int mx = 0; // 记录最大值
for (int i = 0; i < v1.size(); i++) {
if (v1[i] > t) break; // 超过预算限制则停止
int w = t - v1[i]; // 计算剩余的预算
int x = upper_bound(v2.begin(), v2.end(), w) - v2.begin(); // 找到不超过 w 的最大值
x--; // 获取最大值的索引
mx = max(mx, v1[i] + v2[x]); // 更新最大值
}
cout << mx << '\n'; // 输出结果
return 0;
}
python
def dfs1(x, sum):
if x == x1 + 1:
v1.append(sum)
return
dfs1(x + 1, sum + a[x - 1]) # 将 a[x] 改为 a[x - 1]
dfs1(x + 1, sum)
def dfs2(x, sum):
if x == x2 + 1:
v2.append(sum)
return
dfs2(x + 1, sum + b[x - 1]) # 将 b[x] 改为 b[x - 1]
dfs2(x + 1, sum)
n, t = map(int, input().split())
arr = list(map(int, input().split()))
x1 = n // 2
x2 = (n + 1) // 2
a = arr[:x1] # 保持从 0 开始
b = arr[x1:] # 保持从 0 开始
v1, v2 = [], []
dfs1(1, 0)
dfs2(1, 0)
v1.sort()
v2.sort()
v2.append(float('inf'))
mx = 0
for i in range(len(v1)):
if v1[i] > t:
break
w = t - v1[i]
x = next((j for j in range(len(v2)) if v2[j] > w), len(v2)) - 1
mx = max(mx, v1[i] + v2[x])
print(mx)
java
import java.util.*;
public class Main {
static int n, t;
static int[] a = new int[25];
static int[] b = new int[25];
static int x1, x2;
static List<Long> v1 = new ArrayList<>();
static List<Long> v2 = new ArrayList<>();
static void dfs1(int x, long sum) {
if (x == x1 + 1) {
v1.add(sum);
return;
}
dfs1(x + 1, sum + a[x]);
dfs1(x + 1, sum);
}
static void dfs2(int x, long sum) {
if (x == x2 + 1) {
v2.add(sum);
return;
}
dfs2(x + 1, sum + b[x]);
dfs2(x + 1, sum);
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
t = sc.nextInt();
x1 = n / 2;
x2 = (n + 1) / 2;
for (int i = 1; i <= x1; i++) {
a[i] = sc.nextInt();
}
for (int i = 1; i <= x2; i++) {
b[i] = sc.nextInt();
}
dfs1(1, 0);
dfs2(1, 0);
Collections.sort(v1);
Collections.sort(v2);
v2.add(Long.MAX_VALUE);
long mx = 0;
for (long value : v1) {
if (value > t) {
break;
}
long w = t - value;
int x = Collections.binarySearch(v2, w);
if (x < 0) {
x = -(x + 1) - 1;
}
mx = Math.max(mx, value + v2.get(x));
}
System.out.println(mx);
}
}
题目内容
某团队来了一个大项目,该项目已知有n个需求,每个需求工作量分别需要t1、t2、t3.......tn人天,由于该项目需求过多,负责人小明决定先给出T人天预算完成部分需求。对于单个需求,每个任务要么不做,要么全部完成,必须耗时ti人天完成,现在小明想知道T人天的预算最多能做多少人天的需求。
输入描述
输入共两行
首行是2个整数,以空格隔开,分别是n和T,n代表需求总数,T代表工作量评估不超过T人天
次行有n个整数,以空格隔开,分别是t1、t2、t3....tn,代表每个需求所需工作量,单位是人天
数据范围:1≤n≤40;1≤ti≤109;1≤T≤109
输出描述
一个整数Ans,代表T人天的预算最多能做Ans人天的需求
样例1
输入
5 17
2 3 5 11 7
输出
17
说明
该项目有5个需求,工作量评估不超过17人天,每个需求工作量分别需要2人天、3人天、5人天、11人天、7人天;
小明选择需求1、需求2、需求3、需求5,所需工作量总和是2+3+5+7=17
样例2
输入
6 100
1 2 7 5 8 10
输出
33
说明
该项目有6个需求,工作量评估不超过100人天,每个需求工作量分别需要1人天、2人天、7人天、6人天、8人天、10人天;
小明选择全部需求,所需工作量总和是1+2+7+5+8+10=33
样例3
输入
6 100
101 102 103 104 105 106
输出
0
说明
该项目有6个需求,工作量评估不超过100人天,每个需求工作量分别需要101人天、102人天、103人天、104人天、105人天、106人天;
小明无论选择哪个需求都超过了100人天,所需工作量总和最大是0人天