题目:https://www.luogu.org/problemnew/show/P2050
分析:一看见几个厨师同时做就立刻想到了网络流,再一细看和修车那道题挺像的。于是仿照那道题把厨师拆了(-_-||),每个厨师拆成p个点,代表第几个厨师做的倒数第几道菜,然后各自向每一道菜连边,容量为1,费用为j*花费的时间。最后源点向每个厨师拆出来的点连费用为0,容量为1的边;每一道菜向汇点连容量为这道菜出现次数,费用为0的边。跑一遍最小费用最大流就行了。
然而!!!!!!!!!!!
通过计(zhi)算(jue),发现此题最多可有3280040条边,算上反向的。。。。。可怕。难怪会T。
从这里继续想下去,自然想减少边数,但显然每一条边都可能会用到。然后看一眼数据范围,稍一联想,此图最大流必为,即最大为800,所以最多找800次增广路,图中有很多边没有用到。所以我们先不忙把所有的边都加上,只连下一次找增广路可能会用到的边。
那么对于第一次,源点→每个厨师做的倒数第一道菜→每一道菜→汇点,这些边连上。然后每一次寻找增广路必定会过一个”第i个厨师做的倒数第j道菜”这样的点,我们再连源点→第i个厨师做的倒数第j+1道菜→每一道菜。
这样一来图中的边就少了很多,应该就能A了!!!!
然而。。。。。。
什么鬼????好吧无奈只能手写队列过了。。。。。。
代码:
#include <cstdio> #include <cstring> #include <iostream> #include <queue> using namespace std; typedef long long LL; struct Edge { int v, next, cap, cost; Edge(int a = 0, int b = 0, int c = 0, int d = 0) :v(a), next(b), cap(c), cost(d){}; }edge[6600001]; int n, m, s, t, cnt, ans, p_tot; int pre[80050], dist[80050], timeT[42][102], head[80050]; bool inQueue[80050]; int q[80050], headOfQueue, tailOfQueue; void AddEdge(int _u, int _v, int _cap, int _cost) { edge[cnt] = Edge(_v, head[_u], _cap, _cost); head[_u] = cnt++; edge[cnt] = Edge(_u, head[_v], 0, -_cost); head[_v] = cnt++; } void SPFA() { memset(dist, 0x7f, sizeof(dist)); dist[s] = 0; headOfQueue = tailOfQueue = 0; q[tailOfQueue++] = s; while(tailOfQueue > headOfQueue) { int p = q[headOfQueue++]; inQueue[p] = false; for(int i = head[p]; ~i; i = edge[i].next) if(edge[i].cap && dist[p] + edge[i].cost < dist[edge[i].v]) { dist[edge[i].v] = dist[p] + edge[i].cost; pre[edge[i].v] = i; if(!inQueue[edge[i].v]) { inQueue[edge[i].v] = true; q[tailOfQueue++] = edge[i].v; } } } int p = t, record; while(p != s) { int o = pre[p]; if(edge[o ^ 1].v == 0) record = p; edge[o].cap--; edge[o ^ 1].cap++; p = edge[o ^ 1].v; } ans += dist[t]; record += p_tot - 1; int A = record / p_tot, B = record % p_tot + 1; AddEdge(s, (A - 1) * p_tot + B + 1, 1, 0); for(int i = 1; i <= n; i++) AddEdge((A - 1) * p_tot + B + 1, 80000 + i, 1, (B + 1) * timeT[i][A]); } int main() { memset(head, -1, sizeof(head)); scanf("%d%d", &n, &m); s = 0, t = 80041; for(int i = 1; i <= n; i++) { int p; scanf("%d", &p); AddEdge(80000 + i, t, p, 0); p_tot += p; } for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) scanf("%d", &timeT[i][j]); for (int i = 1; i <= m; i++) { AddEdge(s, (i - 1) * p_tot + 1, 1, 0); for(int j = 1; j <= n; j++) AddEdge((i - 1) * p_tot + 1, 80000 + j, 1, timeT[j][i]); } for (int i = 1; i <= p_tot; i++) SPFA(); printf("%d", ans); return 0; }//Rhein_E