概述
题目大意:
有
n
n
科作业,第科作业有
ai
a
i
份,有
m
m
个人,第个人只做其中任意
bj
b
j
份作业,我们知道第
j
j
个人做一份第科作业的时间是
ci,j
c
i
,
j
。
问所有人做作业的总时间的和最小值是多少。
∑ni=1ai=∑mj=1bj
∑
i
=
1
n
a
i
=
∑
j
=
1
m
b
j
n<=200,m<=200,ai,bi<=10000,sum(ai)<=1000000
n
<=
200
,
m
<=
200
,
a
i
,
b
i
<=
10000
,
s
u
m
(
a
i
)
<=
1000000
分析:
很容易想到费用流,
源点
S
S
向所有科目连边,费用为,流量为
a[i]
a
[
i
]
每科向每人连边,费用为
c[i][j]
c
[
i
]
[
j
]
,流量为
a[i]
a
[
i
]
每人向汇点
T
T
连边,费用为,流量为
b[j]
b
[
j
]
但是会超时,
所以我们考虑动态加边,
在之前加入的最短边满流后才加的边
也就是说我们一开始对第
i
i
科只向做这科需要最短时间的人连边
当
j
j
到汇点的边满流时,我们就加入做这科需要时间次短的人,以此类推,当然当下一次加入的人
k
k
到汇点的边依然满流的时候,
i
i
到的边也不能忽略不加,因为网络流中是有反悔边的,所以这条边可能会使结果更优。
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define inf 2333333
#define M 100005
#define N 505
using namespace std;
struct edge { int To, num, cost, nxt;}e[M*2];
struct Node { int num, id;}c[N][N];
int incf[N], tot[N], flow[N], dis[N], pre[N], ls[N], v[N], a[N], b[N], n, m, maxflow, ans, cnt = 1;
void Addedge(int u, int v, int w, int f) {
e[++cnt].To = v; e[cnt].num = w; e[cnt].cost = f; e[cnt].nxt = ls[u]; ls[u] = cnt;
e[++cnt].To = u; e[cnt].num = 0; e[cnt].cost = -f; e[cnt].nxt = ls[v]; ls[v] = cnt;
}
bool cmp(Node aa, Node bb) {
return aa.num < bb.num;
}
bool spfa(int s, int t) {
for (int i = s; i <= t; i++)
v[i] = 0, dis[i] = inf;
queue <int> Q; Q.push(s);
v[s] = 1; dis[s] = 0;
incf[s] = inf;
while (Q.size()) {
int u = Q.front(); Q.pop();
for (int i = ls[u]; i; i = e[i].nxt)
if (e[i].num && dis[e[i].To] > dis[u] + e[i].cost) {
dis[e[i].To] = dis[u] + e[i].cost;
incf[e[i].To] = min(incf[u], e[i].num);
pre[e[i].To] = i;
if (!v[e[i].To]) {
Q.push(e[i].To);
v[e[i].To] = 1;
}
}
v[u] = 0;
}
return dis[t] != inf;
}
void update(int s, int t) {
int x = t;
while (x != s){
int i = pre[x];
e[i].num -= incf[t];
e[i^1].num += incf[t];
x = e[i^1].To;
}
maxflow += incf[t];
ans += dis[t]*incf[t];
}
void edmonds_karp(int s, int t){
while (spfa(s, t)) {
update(s, t);
for (int i = 1; i <= n; i++) {
while (!e[flow[c[i][tot[i]].id]].num && tot[i] < m)
tot[i]++, Addedge(i, n + c[i][tot[i]].id, a[i], c[i][tot[i]].num);
}
}
}
int main() {
scanf("%d%d", &n, &m);
int S = 0, T = n + m + 1;
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= m; i++) scanf("%d", &b[i]);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) scanf("%d", &c[i][j].num), c[i][j].id = j;
sort(c[i]+1, c[i]+m+1, cmp); tot[i] = 1;
Addedge(i, n+c[i][1].id, a[i], c[i][1].num);
}
for (int i = 1; i <= n; i++) Addedge(S, i, a[i], 0);
for (int i = 1; i <= m; i++) Addedge(n+i, T, b[i], 0), flow[i] = cnt - 1;
edmonds_karp(S, T);
printf("%dn", ans);
return 0;
}
最后
以上就是有魅力煎蛋为你收集整理的Jzoj P4371 作业分配___费用流+动态开边题目大意:分析:代码:的全部内容,希望文章能够帮你解决Jzoj P4371 作业分配___费用流+动态开边题目大意:分析:代码:所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复