1 条题解
-
0
解题思路
-
问题建模:
- 将员工及其上下级关系建模为一个有向图,其中节点代表员工,边代表上下级关系( 表示 是 的上级)。
- 解雇一个员工会连带解雇其所有下属(直接或间接),因此需要选择一个闭合子图(closed subset),即选中某些节点后,其所有后继节点也必须被选中。
-
关键观察:
- 目标是选择一个闭合子图,使得子图中所有节点的 之和最大。
- 这是一个典型的最大权闭合子图问题,可以通过网络流中的最小割模型解决。
-
网络流模型:
- 源点 :连接所有 的节点,容量为 。
- 汇点 :所有 的节点连接到 ,容量为 。
- 依赖关系:如果 是 的上级,则添加一条 的边,容量为无穷大(表示必须同时选中)。
- 最大利润为所有正权值之和减去最小割的容量。
-
求解步骤:
- 构造网络流图。
- 使用 Dinic 算法计算从 到 的最大流(即最小割)。
- 最终利润为 。
- 通过残量网络 BFS 标记可达节点,统计最小解雇人数。
解题方法
-
输入处理:
- 读取 和 ,以及每个员工的 。
- 读取 条上下级关系,构建邻接表。
-
网络流建图:
- 源点 ,汇点 。
- 对于 的员工,添加边 ,容量为 。
- 对于 的员工,添加边 ,容量为 。
- 对于每条上下级关系 ,添加边 ,容量为 (代码中用
INF
表示)。
-
计算最大流:
- 使用 Dinic 算法计算从 到 的最大流(即最小割)。
- 最大利润为 。
-
统计最少解雇人数:
- 在残量网络上从 开始 BFS,标记所有可达节点。
- 被标记的节点(除 外)即为需要解雇的员工,数量为
num - 1
。
代码实现要点
- Dinic 算法:
- BFS 分层:计算从源点的最短距离。
- DFS 多路增广:在分层图上寻找增广路。
- 残量网络分析:
- 通过
cal(u)
函数(基于 BFS)统计可达节点。
- 通过
- 复杂度:
- Dinic 算法的时间复杂度为 ,但在实际稀疏图上表现良好。
##C++代码实现:
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<cstdlib> #include<utility> #include<algorithm> #include<utility> #include<queue> #include<vector> #include<set> #include<stack> #include<cmath> #include<map> #include<ctime> #define P pair<int,int> #define ll long long #define ull unsigned long long #define lson id*2,l,mid #define rson id*2+1,mid+1,r #define ls id*2 #define rs id*2+1 #define Mod(a,b) a<b?a:a%b+b using namespace std; const ll M = 1e9 + 7; const ll INF = 0x3f3f3f3f3f3f3f3f; const int N = 5010; const double e = 10e-6; struct edge { int to; ll cap; int rev; }; vector<edge>G[N]; int level[N], iter[N]; void addEdge(int from, int to, ll cap) { G[from].push_back(edge{ to,cap,(int)G[to].size() }); G[to].push_back(edge{ from,0,(int)G[from].size() - 1 }); } void bfs(int s) { memset(level, -1, sizeof(level)); queue<int> que; level[s] = 0; que.push(s); while (!que.empty()) { int v = que.front(); que.pop(); int len = G[v].size(); for (int i = 0; i < len; i++) { edge &e = G[v][i]; if (e.cap > 0 && level[e.to] < 0) { level[e.to] = level[v] + 1; que.push(e.to); } } } } ll dfs(int v, int t, ll f) { if (v == t) return f; int len = G[v].size(); for (int &i = iter[v]; i < len; i++) { edge &e = G[v][i]; if (e.cap > 0 && level[v] < level[e.to]) { ll d = dfs(e.to, t, min(f, e.cap)); if (d > 0) { e.cap -= d; G[e.to][e.rev].cap += d; return d; } } } return 0; } ll maxFlow(int s, int t) { ll flow = 0; while (1) { bfs(s); if (level[t] < 0) return flow; memset(iter, 0, sizeof(iter)); ll f; while ((f = dfs(s, t, INF)) > 0) flow += f; } } int n, m, num; bool vis[N]; void cal(int u) { vis[u] = true; num++; int len = G[u].size(); for (int i = 0; i < len; i++) { edge e = G[u][i]; if (e.cap > 0 && !vis[e.to]) cal(e.to); } } int main() { while (~scanf("%d%d", &n, &m)) { for (int i = 0; i <= n + 1; i++) G[i].clear(); ll xx, ans = 0; for (int i = 1; i <= n; i++) { scanf("%lld", &xx); if (xx > 0) { addEdge(0, i, xx); ans += xx; } else if (xx < 0) addEdge(i, n + 1, -xx); } int x, y; for (int i = 1; i <= m; i++) { scanf("%d%d", &x, &y); addEdge(x, y, INF); } ans -= maxFlow(0, n + 1); memset(vis, false, sizeof(vis)); num = 0; cal(0); printf("%d %lld\n", num - 1, ans); } return 0; }
-
- 1
信息
- ID
- 1988
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者