题源
题意:
有
k
k
个程序分配了若干个线程任务,每个程序完成一定的线程时会打印出任务进度,任务不会超过一次被执行而且属于同一个程序的任务进度一定是严格递增的,现在个程序打印出
n
n
个任务进度,问最少/最多还有多少线程任务没完成。
思路:
可以用费用流去做,因为每个进度最多在一个程序里面显示一次而且在同一个程序里面是严格递增的,那么可以这样建图:
- 源点向每个进度连边,容量为 1 1 , 费用为,代表该进度最多打印一次。
- 每个程序向汇点连边,容量为 1 1 ,费用为, 代表一个程序只能有一种严格递增的任务进度。
- 每个进度和它后面的大于自己的进度连边,容量为 1 1 ,费用为,代表该进度可以在后面那个进度之前打印。
- 每个任务进度和分配任务大于该打印进度的程序连边,容量为 1 1 ,费用为当前进度值,代表该进度可以被该程序打印,而且这里就是终点。
- 每个进度要拆成入点和出点,入点和出点连一条边,容量为,费用为负无穷,因为每个进度一定要被打印一次,该边一定要经过而且流量为
1
1
。
如果求剩余最大就求一下最小费用流,求最小值就在 4 4 中的步骤将费用置为负值,求一下费用流,最后加上之前置为负无穷的边,因为不能确定流量大小,可以枚举,只有满足必流边满流就是一种合法答案,最后取下所有的最小值即可。
#include<bits/stdc++.h>
typedef long long ll;
const int maxn = 3e2 + 10;
const ll INF = 1e12 + 10;
using namespace std;
struct P {
int from, to;
ll cap, flow, cost;
P() {}
P(int f, int t, ll ca, ll fl, ll c) {
from = f; to = t;
flow = fl; cost = c;
cap = ca;
}
};
vector<P> edge;
vector<int> G[maxn];
int kase = 1, s, t, n, m, T;
ll inq[maxn], d[maxn];
ll p[maxn], a[maxn];
ll flow, cost;
void add(int f, int t, ll c, ll co) {
edge.push_back(P(f, t, c, 0, co));
edge.push_back(P(t, f, 0, 0, -co));
int sz = edge.size();
G[f].push_back(sz - 2);
G[t].push_back(sz - 1);
}
bool bellmanford(int s, int t, ll &flow, ll &cost) {
fill(d, d + maxn, INF);
memset(inq, 0, sizeof(inq));
d[s] = 0; inq[s] = 1;
p[s] = 0; a[s] = INF;
queue<int> q; q.push(s);
while(!q.empty()) {
int u = q.front(); q.pop();
inq[u] = 0;
for(int i = 0; i < G[u].size(); i++) {
P &e = edge[G[u][i]];
if(e.cap <= e.flow || d[e.to] <= d[u] + e.cost) continue;
d[e.to] = d[u] + e.cost;
p[e.to] = G[u][i];
a[e.to] = min(a[u], e.cap - e.flow);
if(inq[e.to]) continue;
q.push(e.to); inq[e.to] = 1;
}
}
if(d[t] == INF) return false;
flow += a[t]; cost += d[t] * a[t];
for(int u = t; u != s; u = edge[p[u]].from) {
edge[p[u]].flow += a[t];
edge[p[u] ^ 1].flow -= a[t];
}
return true;
}
ll mincost(int s, int t) {
flow = 0; cost = 0;
while(bellmanford(s, t, flow, cost));
return cost;
}
ll A[maxn], B[maxn], ans;
bool build_graph(int flag, int mid) {
edge.clear();
for(int i = 0; i < maxn; i++) G[i].clear();
s = 0; t = n + 2 * m + 1;
for(int i = 1; i <= m; i++) {
for(int j = i + 1; j <= m; j++) {
if(B[i] >= B[j]) continue;
add(n + 2 * i, n + 2 * j - 1, 1, 0);
}
}
for(int i = 1; i <= m; i++) { add(n + 2 * i - 1, n + 2 * i, 1, -INF); add(s, n + 2 * i - 1, 1, 0); }
for(int i = 1; i <= n; i++) add(i, t, 1, 0);
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
int from = n + 2 * j, to = i;
if(A[i] >= B[j]) add(from, to, 1, B[j] * flag);
}
}
s = t + 1; add(s, 0, mid, 0);
ans = mincost(s, t) + INF * m;
for(int i = 0; i < edge.size(); i++) if(edge[i].cost == -INF && edge[i].flow != edge[i].cap) return false; ///必流边没流
return true;
}
int main() {
freopen("threads.in", "r", stdin);
scanf("%d", &T);
while(T--) {
scanf("%d %d", &m, &n);
ll sum = 0, ans1 = INF, ans2 = INF;
for(int i = 1; i <= n; i++) { scanf("%lld", &A[i]); sum += A[i]; }
for(int i = 1; i <= m; i++) scanf("%lld", &B[i]);
for(int i = 1; i <= n; i++) {
if(build_graph(1, i)) ans1 = min(ans1, ans);
if(build_graph(-1, i)) ans2 = min(ans2, ans);
}
cout << sum + ans2 << " " << sum - ans1 << endl;
}
return 0;
}