[BZOJ2879][Noi2012]美食节
试题描述
CZ市为了欢迎全国各地的同学,特地举办了一场盛大的美食节。作为一个喜欢尝鲜的美食客,小M自然不愿意错过这场盛宴。他很快就尝遍了美食节所有的美食。然而,尝鲜的欲望是难以满足的。尽管所有的菜品都很可口,厨师做菜的速度也很快,小M仍然觉得自己桌上没有已经摆在别人餐桌上的美食是一件无法忍受的事情。于是小M开始研究起了做菜顺序的问题,即安排一个做菜的顺序使得同学们的等待时间最短。小M发现,美食节共有 (n) 种不同的菜品。每次点餐,每个同学可以选择其中的一个菜品。总共有 (m) 个厨师来制作这些菜品。当所有的同学点餐结束后,菜品的制作任务就会分配给每个厨师。然后每个厨师就会同时开始做菜。厨师们会按照要求的顺序进行制作,并且每次只能制作一人份。此外,小M还发现了另一件有意思的事情: 虽然这 (m) 个厨师都会制作全部的 (n) 种菜品,但对于同一菜品,不同厨师的制作时间未必相同。他将菜品用 (1, 2, ..., n) 依次编号,厨师用 (1, 2, ..., m) 依次编号,将第 (j) 个厨师制作第 (i) 种菜品的时间记为 (t_{i,j}) 。小M认为:每个同学的等待时间为所有厨师开始做菜起,到自己那份菜品完成为止的时间总长度。换句话说,如果一个同学点的菜是某个厨师做的第 (k) 道菜,则他的等待时间就是这个厨师制作前 (k) 道菜的时间之和。而总等待时间为所有同学的等待时间之和。现在,小M找到了所有同学的点菜信息: 有 (p_i) 个同学点了第i种菜品((i=1, 2, ..., n))。他想知道的是最小的总等待时间是多少。
输入
输入文件的第 (1) 行包含两个正整数 (n) 和 (m),表示菜品的种数和厨师的数量。 第 (2) 行包含 (n) 个正整数,其中第 (i) 个数为 (p_i),表示点第 (i) 种菜品的人数。 接下来有 (n) 行,每行包含 (m) 个非负整数,这 (n) 行中的第 (i) 行的第 (j) 个数为 (t_{i,j}),表示第 (j) 个厨师制作第 (i) 种菜品所需的时间。 输入文件中每行相邻的两个数之间均由一个空格隔开,行末均没有多余空格。
输出
输出仅一行包含一个整数,为总等待时间的最小值。
输入示例
3 2
3 1 1
5 7
3 6
8 9
输出示例
47
数据规模及约定
对于 (100 exttt{%}) 的数据,(n le 40, m le 100, p le 800, t_{i,j} le 1000)(其中 (p = sum {p_i}),即点菜同学的总人数)。
题解
建图同[BZOJ1070][SCOI2007]修车,这题需要动态建图,对于厨师的每个时间点,如果第 (i) 个时间点的厨师被流经过了,就把第 (i+1) 个时间点的厨师对应的节点加进来,并把这个节点相关的所有边都加进来。
这题,洛谷上似乎卡 SLF 优化?
而且我把动态加点改称动态加边会 WA?
常数似乎很重要啊?
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--)
const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *Tail;
inline char Getchar() {
if(Head == Tail) {
int l = fread(buffer, 1, BufferSize, stdin);
Tail = (Head = buffer) + l;
}
return *Head++;
}
int read() {
int x = 0, f = 1; char c = Getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }
return x * f;
}
#define maxn 80050
#define maxm 6000010
#define oo 2147483647
struct Edge {
int from, to, flow, cost;
Edge() {}
Edge(int _1, int _2, int _3, int _4): from(_1), to(_2), flow(_3), cost(_4) {}
};
#define maxdish 45
#define maxcook 110
#define maxstu 810
int CntP, dish, cook, stu;
struct Point {
int id;
Point(): id(0) {}
int p() { return id ? id : id = ++CntP; }
} Dish[maxdish], Cook[maxcook][maxstu], S, T;
int Now[maxn], CookId[maxn], Time[maxdish][maxcook];
struct ZKW {
int n, m, s, t, cost, ans, head[maxn], nxt[maxm];
Edge es[maxm];
int d[maxn], Q[maxn], hd, tl;
bool inq[maxn];
bool vis[maxn], ForAdd[maxn];
void init() {
m = 0; memset(head, -1, sizeof(head));
return ;
}
void setn(int _) {
n = _;
return ;
}
void AddEdge(int a, int b, int c, int d) {
es[m] = Edge(a, b, c, d); nxt[m] = head[a]; head[a] = m++;
es[m] = Edge(b, a, 0, -d); nxt[m] = head[b]; head[b] = m++;
return ;
}
void UseBackup(int u) {
if(!Now[u]) return ;
int now = Cook[CookId[u]][Now[u]+1].p();
Now[now] = Now[u] + 1; CookId[now] = CookId[u];
rep(i, 1, dish) AddEdge(Dish[i].p(), now, 1, Time[i][CookId[now]] * Now[now]);
AddEdge(now, T.p(), 1, 0);
n = CntP;
Now[u] = 0;
return ;
}
bool BFS() {
rep(i, 1, n) d[i] = oo;
d[t] = 0;
hd = tl = 0; Q[++tl] = t; inq[t] = 1;
while(hd < tl) {
int u = Q[++hd]; inq[u] = 0;
for(int i = head[u]; i != -1; i = nxt[i]) {
Edge& e = es[i^1];
if(d[e.from] > d[u] + e.cost && e.flow) {
d[e.from] = d[u] + e.cost;
if(!inq[e.from]) {
inq[e.from] = 1;
Q[++tl] = e.from;
}
}
}
}
if(d[s] == oo) return 0;
cost = d[s];
return 1;
}
int DFS(int u, int a) {
if(u == t || !a) return ans += cost * a, a;
if(vis[u]) return 0;
vis[u] = 1;
int flow = 0, f;
for(int i = head[u]; i != -1; i = nxt[i]) {
Edge& e = es[i];
if(d[e.to] == d[u] - e.cost && (f = DFS(e.to, min(a, e.flow)))) {
ForAdd[u] = ForAdd[e.to] = 1;
flow += f; a -= f;
e.flow -= f; es[i^1].flow += f;
if(!a) return flow;
}
}
return flow;
}
int MaxFlow(int _s, int _t) {
s = _s; t = _t;
int flow = 0, f;
while(BFS()) {
memset(ForAdd, 0, sizeof(ForAdd));
do {
memset(vis, 0, sizeof(vis));
f = DFS(s, oo);
flow += f;
} while(f);
int N = n;
rep(i, 1, N) if(ForAdd[i]) UseBackup(i);
}
return flow;
}
} sol;
int main() {
dish = read(); cook = read();
sol.init();
rep(i, 1, dish) {
int p = read(); stu += p;
sol.AddEdge(S.p(), Dish[i].p(), p, 0);
}
rep(i, 1, dish) rep(j, 1, cook) {
Time[i][j] = read();
sol.AddEdge(Dish[i].p(), Cook[j][1].p(), 1, Time[i][j]);
}
rep(j, 1, cook) {
sol.AddEdge(Cook[j][1].p(), T.p(), 1, 0);
Now[Cook[j][1].p()] = 1; CookId[Cook[j][1].p()] = j;
}
sol.setn(CntP);
sol.MaxFlow(S.p(), T.p());
printf("%d
", sol.ans);
return 0;
}