题目: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了!!!!
然而。。。。。。
什么鬼????好吧无奈只能手写队列过了。。。。。。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <queue> 5 using namespace std; 6 7 typedef long long LL; 8 struct Edge 9 { 10 int v, next, cap, cost; 11 Edge(int a = 0, int b = 0, int c = 0, int d = 0) 12 :v(a), next(b), cap(c), cost(d){}; 13 }edge[6600001]; 14 int n, m, s, t, cnt, ans, p_tot; 15 int pre[80050], dist[80050], timeT[42][102], head[80050]; 16 bool inQueue[80050]; 17 int q[80050], headOfQueue, tailOfQueue; 18 19 void AddEdge(int _u, int _v, int _cap, int _cost) 20 { 21 edge[cnt] = Edge(_v, head[_u], _cap, _cost); 22 head[_u] = cnt++; 23 edge[cnt] = Edge(_u, head[_v], 0, -_cost); 24 head[_v] = cnt++; 25 } 26 void SPFA() 27 { 28 memset(dist, 0x7f, sizeof(dist)); 29 dist[s] = 0; 30 headOfQueue = tailOfQueue = 0; 31 q[tailOfQueue++] = s; 32 while(tailOfQueue > headOfQueue) 33 { 34 int p = q[headOfQueue++]; 35 inQueue[p] = false; 36 for(int i = head[p]; ~i; i = edge[i].next) 37 if(edge[i].cap && dist[p] + edge[i].cost < dist[edge[i].v]) 38 { 39 dist[edge[i].v] = dist[p] + edge[i].cost; 40 pre[edge[i].v] = i; 41 if(!inQueue[edge[i].v]) 42 { 43 inQueue[edge[i].v] = true; 44 q[tailOfQueue++] = edge[i].v; 45 } 46 } 47 } 48 int p = t, record; 49 while(p != s) 50 { 51 int o = pre[p]; 52 if(edge[o ^ 1].v == 0) record = p; 53 edge[o].cap--; 54 edge[o ^ 1].cap++; 55 p = edge[o ^ 1].v; 56 } 57 ans += dist[t]; 58 record += p_tot - 1; 59 int A = record / p_tot, B = record % p_tot + 1; 60 61 AddEdge(s, (A - 1) * p_tot + B + 1, 1, 0); 62 for(int i = 1; i <= n; i++) 63 AddEdge((A - 1) * p_tot + B + 1, 80000 + i, 1, (B + 1) * timeT[i][A]); 64 } 65 int main() 66 { 67 memset(head, -1, sizeof(head)); 68 scanf("%d%d", &n, &m); 69 s = 0, t = 80041; 70 for(int i = 1; i <= n; i++) 71 { 72 int p; 73 scanf("%d", &p); 74 AddEdge(80000 + i, t, p, 0); 75 p_tot += p; 76 } 77 for(int i = 1; i <= n; i++) 78 for(int j = 1; j <= m; j++) 79 scanf("%d", &timeT[i][j]); 80 for (int i = 1; i <= m; i++) 81 { 82 AddEdge(s, (i - 1) * p_tot + 1, 1, 0); 83 for(int j = 1; j <= n; j++) 84 AddEdge((i - 1) * p_tot + 1, 80000 + j, 1, timeT[j][i]); 85 } 86 for (int i = 1; i <= p_tot; i++) 87 SPFA(); 88 printf("%d", ans); 89 90 return 0; 91 }//Rhein_E