[luoguP2053] [SCOI2007]修车(最小费用最大流)

传送门

 

网络流的建图真的好难!

将一个点拆分成多个点的思想还需要加强。

题解

代码和题解中的图略不一样。

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#define N 1000001

using namespace std;

int n, m, cnt, s, t;
int head[N], to[N], nex[N], val[N], cost[N], dis[N], pre[N], path[N];
bool vis[N];
double tim;

inline int read()
{
	int x = 0, f = 1;
	char ch = getchar();
	for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
	for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
	return x * f;
}

inline void add(int x, int y, int z, int v)
{
	to[cnt] = y;
	val[cnt] = z;
	cost[cnt] = v;
	nex[cnt] = head[x];
	head[x] = cnt++;
}

inline bool spfa()
{
	int i, u, v;
	queue <int> q;
	memset(vis, 0, sizeof(vis));
	memset(pre, -1, sizeof(pre));
	memset(dis, 127, sizeof(dis));
	q.push(s);
	dis[s] = 0;
	while(!q.empty())
	{
		u = q.front();
		vis[u] = 0;
		q.pop();
		for(i = head[u]; ~i; i = nex[i])
		{
			v = to[i];
			if(val[i] && dis[v] > dis[u] + cost[i])
			{
				dis[v] = dis[u] + cost[i];
				pre[v] = u;
				path[v] = i;
				if(!vis[v])
				{
					q.push(v);
					vis[v] = 1;
				}
			}
		}
	}
	return pre[t] != -1;
}

int main()
{
	int i, j, k, x, f;
	m = read();
	n = read();
	s = 0, t = n + n * m + 1;
	memset(head, -1, sizeof(head));
	for(i = 1; i <= n; i++)
		for(j = 1; j <= m; j++)
		{
			x = read();
			for(k = 1; k <= n; k++)
			{
				add((k - 1) * m + j, i + n * m, 1, k * x);
				add(i + n * m, (k - 1) * m + j, 0, -k * x);
			}
			add(s, j + (i - 1) * m, 1, 0);
			add(j + (i - 1) * m, s, 0, 0);
		}
	for(i = 1; i <= n; i++)
	{
		add(i + n * m, t, 1, 0);
		add(t, i + n * m, 0, 0);
	}
	while(spfa())
	{
		f = 1e9;
		for(i = t; i != s; i = pre[i]) f = min(f, val[path[i]]);
		tim += dis[t];
		for(i = t; i != s; i = pre[i])
		{
			val[path[i]] -= f;
			val[path[i] ^ 1] += f;
		}
	}
	printf("%.2lf\n", double(tim / n));
	return 0;
}

  

posted @ 2018-01-09 08:05  zht467  阅读(112)  评论(0编辑  收藏  举报