洛谷P2050[NOI2012]美食节(网络流+动态加边优化)

题目:https://www.luogu.org/problemnew/show/P2050

imageimageimage


分析:一看见几个厨师同时做就立刻想到了网络流,再一细看和修车那道题挺像的。于是仿照那道题把厨师拆了(-_-||),每个厨师拆成p个点,代表第几个厨师做的倒数第几道菜,然后各自向每一道菜连边,容量为1,费用为j*花费的时间。最后源点向每个厨师拆出来的点连费用为0,容量为1的边;每一道菜向汇点连容量为这道菜出现次数,费用为0的边。跑一遍最小费用最大流就行了。

然而!!!!!!!!!!!

image

通过计(zhi)算(jue),发现此题最多可有3280040条边,算上反向的。。。。。可怕。难怪会T。

从这里继续想下去,自然想减少边数,但显然每一条边都可能会用到。然后看一眼数据范围,稍一联想,此图最大流必为image,即最大为800,所以最多找800次增广路,图中有很多边没有用到。所以我们先不忙把所有的边都加上,只连下一次找增广路可能会用到的边

那么对于第一次,源点→每个厨师做的倒数第一道菜→每一道菜→汇点,这些边连上。然后每一次寻找增广路必定会过一个”第i个厨师做的倒数第j道菜”这样的点,我们再连源点→第i个厨师做的倒数第j+1道菜→每一道菜。

这样一来图中的边就少了很多,应该就能A了!!!!

然而。。。。。。

image

什么鬼????好吧无奈只能手写队列过了。。。。。。

代码:

  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
View Code
posted @ 2018-07-12 09:22  Rhein_E  阅读(358)  评论(0)    收藏  举报