洛谷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

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

代码:

#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

猜你喜欢

转载自www.cnblogs.com/Rhein-E/p/9297809.html