您的位置:首页 > 运维架构

NOI2012 美食节 动态加边维护费用流

2016-08-19 22:20 363 查看

题目大意

现在有N种人,每种人有Pi个,还有M个厨师,第j个厨师给第i种人做菜需要时间Ti,j,并且同一时间一个厨师只能做一道菜。每个人的权值是这个人等待的时间。问所有人的权值和最小是多少。

N≤40

M≤100

∑Pi≤800

解题思路

设Sum=∑Pi。

我们考虑用网络流做这题,我们把第i个厨师差拆成Sum个点,第k个点表示这个厨师做的倒数第k道菜,每个点向每个人j连一条流量为1,费用为Tj,i∗k,向T连一条流量为1,费用为0的边。每个人想S连一条流量为Pi,费用为0的边。显然跑一次最小费用最大流的费用就是答案。

但是这样肯定会超时!因为边太多了。所以我们可以考虑动态加边,对于每个厨师,做完代表倒数k个菜的点才把代表倒数k+1个菜的点加进去即动态加边,就能大大减少网络流遍历边的时间,并且我们不用但新是否会流了代表倒数第k个菜的点,但是没流代表倒数第k−1个菜的点,因为我们肯定会优先流费用小的边。

程序

//YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;

const int MAXN = 100000;

int All, S, T, N, M, Min, P[50], t[50][1000], D[MAXN];
int tot, Last[MAXN], Go[MAXN * 2], Len[MAXN * 2], Next[MAXN * 2], Cost[MAXN * 2], Dis[MAXN];
bool Flag[MAXN], Bz[MAXN];

void Open(int u, int v, int flow, int cost) {
Next[++ tot] = Last[u], Last[u] = tot, Go[tot] = v, Len[tot] = flow, Cost[tot] = cost;
}

void Link(int u, int v, int flow, int cost) {
Open(u, v, flow, cost);
Open(v, u, 0, -cost);
}

void Add(int Num, int Cnt) {
int Ord = (Num - 1) * All + Cnt;
if (Cnt > All || Flag[Ord]) return;
Flag[Ord] = 1;
Link(S, Ord, 1, 0);
for (int i = 1; i <= N; i ++) Link(Ord, All * M + i, 1, t[i][Num] * Cnt);
}

int Dfs(int Now, int flow) {
if (Now == T) {
Min += Dis[T] * flow;
return flow;
}
int Use = 0;
Bz[Now] = 1;
for (int p = Last[Now]; p; p = Next[p]) {
int v = Go[p];
if (Dis[v] == Dis[Now] + Cost[p] && Len[p] > 0 && !Bz[v]) {
int t = Dfs(v, min(flow - Use, Len[p]));
Len[p] -= t, Len[p ^ 1] += t, Use += t;
if (Use == flow) {
if (Now <= All * M && Now != S) Add((Now - 1) / All + 1,(Now - 1) % All + 2);
return Use;
}
}
}
return Use;
}

void Go_Flow() {
int Cnt = 0;
while (1 == 1) {
memset(Bz, 0, sizeof Bz), memset(Dis, 60, sizeof Dis);
int Inf = Dis[0];
int l = 0, r = 1;
D[1] = S, Dis[S] = 0;
while (l != r) {
l = (l + 1) % MAXN;
int Now = D[l];
for (int p = Last[Now]; p; p = Next[p]) {
int v = Go[p];
if (Len[p] && Dis[v] > Dis[Now] + Cost[p]) {
Dis[v] = Dis[Now] + Cost[p];
if (!Bz[v]) {
Bz[v] = 1;
r = (r + 1) % MAXN;
D[r] = v;
}
}
}
Bz[Now] = 0;
}
if (Dis[T] == Inf) return;
Dfs(S, Inf);
}
}

int main() {
scanf("%d%d", &N, &M);
for (int i = 1; i <= N; i ++) {
scanf("%d", &P[i]);
All += P[i];
}

S = 0, T = All * M + N + 1;
tot = 1;
for (int i = 1; i <= N; i ++) {
Link(All * M + i, T, P[i], 0);
for (int j = 1; j <= M; j ++) scanf("%d", &t[i][j]);
}
for (int i = 1; i <= M; i ++) Add(i, 1);

Go_Flow();
printf("%d\n", Min);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: