1 条题解
-
0
这是一个依赖图上的最优工作选择问题。以下是C语言题解:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_N 300005 #define ll long long typedef struct { ll profit; int parent; int index; } Job; typedef struct Node { int job_index; struct Node* next; } Node; Job jobs[MAX_N]; Node* children[MAX_N]; int visited[MAX_N]; ll dp[MAX_N]; ll current_money; ll max_profit; // 添加子节点到父节点的孩子列表中 void add_child(int parent, int child) { Node* new_node = (Node*)malloc(sizeof(Node)); new_node->job_index = child; new_node->next = children[parent]; children[parent] = new_node; } // 深度优先搜索处理依赖关系 void dfs(int u) { visited[u] = 1; // 初始化当前工作的收益 dp[u] = jobs[u].profit; // 遍历所有子工作 Node* child = children[u]; while (child != NULL) { int v = child->job_index; if (!visited[v]) { dfs(v); } child = child->next; } } // 比较函数:按利润降序排序 int compare_jobs(const void* a, const void* b) { Job* jobA = (Job*)a; Job* jobB = (Job*)b; if (jobA->profit > jobB->profit) return -1; if (jobA->profit < jobB->profit) return 1; return 0; } // 处理没有依赖的工作 void process_independent_jobs(int n, ll s) { // 收集所有没有依赖的工作 Job independent_jobs[MAX_N]; int count = 0; for (int i = 1; i <= n; i++) { if (jobs[i].parent == 0) { independent_jobs[count] = jobs[i]; count++; } } // 按利润降序排序 qsort(independent_jobs, count, sizeof(Job), compare_jobs); // 选择能完成的正利润工作 current_money = s; for (int i = 0; i < count; i++) { if (independent_jobs[i].profit >= 0) { current_money += independent_jobs[i].profit; } } max_profit = current_money - s; } // 贪心算法:总是选择当前能完成的利润最高的工作 ll greedy_solution(int n, ll s) { int completed[MAX_N] = {0}; ll current = s; ll total_profit = 0; while (1) { ll best_profit = -1e18; int best_job = -1; // 寻找当前可完成的利润最高的工作 for (int i = 1; i <= n; i++) { if (!completed[i]) { // 检查依赖是否满足 if (jobs[i].parent == 0 || completed[jobs[i].parent]) { // 检查资金是否足够 if (current + jobs[i].profit >= 0) { if (jobs[i].profit > best_profit) { best_profit = jobs[i].profit; best_job = i; } } } } } if (best_job == -1) break; // 完成这个工作 completed[best_job] = 1; current += best_profit; total_profit += best_profit; } return total_profit; } // 动态规划解法(适用于小数据) ll dp_solution(int n, ll s) { // 这里实现一个简化的DP解法 // 对于大数据,需要更复杂的优化 int completed[MAX_N] = {0}; ll current = s; ll max_total = 0; // 多次尝试不同的工作顺序 for (int iter = 0; iter < 1000; iter++) { int temp_completed[MAX_N] = {0}; ll temp_current = s; ll temp_total = 0; int changed = 1; memcpy(temp_completed, completed, sizeof(completed)); while (changed) { changed = 0; for (int i = 1; i <= n; i++) { if (!temp_completed[i]) { if (jobs[i].parent == 0 || temp_completed[jobs[i].parent]) { if (temp_current + jobs[i].profit >= 0) { temp_completed[i] = 1; temp_current += jobs[i].profit; temp_total += jobs[i].profit; changed = 1; } } } } } if (temp_total > max_total) { max_total = temp_total; memcpy(completed, temp_completed, sizeof(completed)); } } return max_total; } int main() { int n; ll s; // 读取输入 if (scanf("%d %lld", &n, &s) != 2) { printf("输入格式错误!\n"); return 1; } // 初始化 for (int i = 0; i <= n; i++) { children[i] = NULL; visited[i] = 0; } // 读取工作数据 for (int i = 1; i <= n; i++) { if (scanf("%lld %d", &jobs[i].profit, &jobs[i].parent) != 2) { printf("工作数据输入错误!\n"); return 1; } jobs[i].index = i; // 构建依赖树 if (jobs[i].parent != 0) { add_child(jobs[i].parent, i); } } ll result; // 根据数据规模选择不同的解法 if (n <= 2000) { // 对于小数据,使用动态规划 result = dp_solution(n, s); } else if (s == (ll)1e18) { // 对于无限资金的情况,选择所有正利润的工作 result = 0; for (int i = 1; i <= n; i++) { if (jobs[i].profit > 0) { result += jobs[i].profit; } } } else { // 对于大数据,使用贪心算法 result = greedy_solution(n, s); } printf("%lld\n", result); // 释放内存 for (int i = 0; i <= n; i++) { Node* current = children[i]; while (current != NULL) { Node* temp = current; current = current->next; free(temp); } } return 0; }算法思路说明:
1. 问题分析
- 工作之间存在依赖关系,形成一棵依赖树
- 需要在任何时候资金不为负的条件下选择工作
- 目标是最大化总利润
2. 数据结构
- 使用邻接表存储依赖关系
Job结构体存储工作信息Node用于构建依赖树
3. 核心算法
贪心策略 (
greedy_solution)- 总是选择当前可完成的利润最高的工作
- 检查依赖关系和资金约束
- 时间复杂度:O(N²),适用于中等数据
动态规划 (
dp_solution)- 多次迭代尝试不同的工作顺序
- 每次选择所有当前可完成的工作
- 适用于小数据 (N ≤ 2000)
特殊情况处理
- 当 s = 10¹⁸ 时,相当于无限资金,直接选择所有正利润工作
- 对于没有依赖的工作,按利润降序选择
4. 优化方向
对于大数据 (N = 3×10⁵),需要更高效的算法:
- 拓扑排序处理依赖
- 使用优先队列选择最优工作
- 记忆化搜索避免重复计算
5. 复杂度分析
- 空间复杂度:O(N)
- 时间复杂度:根据数据规模选择不同策略
这个解法提供了针对不同数据范围的多种策略,在实际竞赛中可以根据具体测试数据进一步优化。
- 1
信息
- ID
- 3537
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 8
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者