题目大意:好长,如果不想看可以先看看修车那个题,基本一样。
思路:做过修车就好办了。这个题仅仅是数据范围变大了一坨。建图就不说了,主要是动态加边。倒过来做,因为一个厨师最后一个菜做的时间是不会影响到其他菜的时间的。而且每一个厨师确定了最后一个菜才能去想倒数第二个菜是什么。所以每跑一次SPFA,就回来看看是哪个厨师做的菜,然后在多加一个点限制一下流量,将这个点连向所有的菜。
CODE:
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 110
#define MAXP 100010
#define MAXE 200010
#define INF 0x3f3f3f3f
#define S 0
#define T (MAXP - 1)
using namespace std;
int cooks,chefs;
int need[MAX];
int src[MAX][MAX],cnt;
int now[MAX];
struct MinCostMaxFlow{
int head[MAXP],total;
int _next[MAXE],aim[MAXE],flow[MAXE],cost[MAXE];
int f[MAXP],from[MAXP],p[MAXP];
bool v[MAXP];
MinCostMaxFlow() {
total = 1;
}
void Add(int x,int y,int f,int c) {
_next[++total] = head[x];
aim[total] = y;
flow[total] = f;
cost[total] = c;
head[x] = total;
}
void Insert(int x,int y,int f,int c) {
Add(x,y,f,c);
Add(y,x,0,-c);
}
void Initialize() {
for(int i = 1; i <= chefs; ++i) {
Insert(i,++cnt,1,0);
for(int j = 1; j <= cooks; ++j)
Insert(cnt,chefs + j,1,src[j][i]);
}
}
bool SPFA() {
static queue<int> q;
while(!q.empty()) q.pop();
memset(f,0x3f,sizeof(f));
f[S] = 0;
q.push(S);
while(!q.empty()) {
int x = q.front(); q.pop();
v[x] = false;
for(int i = head[x]; i; i = _next[i])
if(flow[i] && f[aim[i]] > f[x] + cost[i]) {
f[aim[i]] = f[x] + cost[i];
if(!v[aim[i]]) {
v[aim[i]] = true;
q.push(aim[i]);
}
from[aim[i]] = x;
p[aim[i]] = i;
}
}
return f[T] != INF;
}
int EdmondsKarp() {
int re = 0;
while(SPFA()) {
int remain_flow = INF;
for(int i = T; i != S; i = from[i])
remain_flow = min(remain_flow,flow[p[i]]);
static int k,temp;
for(int i = T; i != S; i = from[i]) {
flow[p[i]] -= remain_flow;
flow[p[i]^1] += remain_flow;
if(i <= chefs && i >= 1) k = i;
if(i != T && i > chefs) temp = i;
}
++now[k];
Insert(k,++cnt,1,0);
for(int i = 1; i <= cooks; ++i)
Insert(cnt,chefs + i,1,(now[k] + 1) * src[i][k]);
re += f[T] * remain_flow;
}
return re;
}
}solver;
int main()
{
cin >> cooks >> chefs;
for(int i = 1; i <= cooks; ++i)
scanf("%d",&need[i]);
for(int i = 1; i <= cooks; ++i)
for(int j = 1; j <= chefs; ++j)
scanf("%d",&src[i][j]);
for(int i = 1; i <= chefs; ++i)
solver.Insert(S,++cnt,INF,0);
int p = 0;
for(int i = 1; i <= cooks; ++i) {
solver.Insert(++cnt,T,need[i],0);
p += need[i];
}
solver.Initialize();
cout << solver.EdmondsKarp() << endl;
return 0;
}