Poj2175(费用流,负环消圈)

时间:2022-10-27 09:48:49

挺好的题 充分利用了spfa 判断最费用流是否最优的充分必要条件是——图中是否存在负环  如果存在说明最费用流最优否则相反

/*
* this code is made by LinMeiChen
* Problem:
* Type of Problem: 最小费用流
* Thinking: 先按照市*的方发建图判断是否可行,如果不行这用自己的方法
* Feeling:
*/
#include<iostream>
#include<algorithm>
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<math.h>
#include<string>
#include<vector>
#include<queue>
#include<list>
using namespace std;
typedef long long lld;
typedef unsigned int ud;
#define oo 0x3f3f3f3f
#define eatline() char chch;while((chch=getchar())!='\n')continue;
#define MemsetMax(a) memset(a,0x3f,sizeof a)
#define MemsetZero(a) memset(a,0,sizeof a)
#define MemsetMin(a) memset(a,-1,sizeof a)
#define MemsetFalse(a) MemsetZero(a)
#define PQ priority_queue
#define Q queue
#define maxn 250
#define maxm 500000
struct Edge
{
	int v, f, c, next;
}E[maxm];
int tol, n, m, cnt[maxn];
int head[maxn], pre[maxn];
int dis[maxn], mark[maxn];
int q[maxn*maxm], front, rear;
int line[maxn][maxn];
int sum[maxn];

struct Node
{
	int x, y, c;
};
Node bg[maxn], home[maxn];

int getCost(Node& n1, Node& n2)
{
	return abs(n1.x - n2.x) + abs(n1.y - n2.y) + 1;
}

int Spfa(int s, int t)
{
	memset(pre, -1, sizeof pre);
	memset(mark, 0, sizeof mark);
	memset(cnt, 0, sizeof cnt);
	fill(dis, dis + maxn, oo);
	dis[t] = 0;
	mark[t] = 1;
	cnt[t]++;
	front = rear = 0;
	q[rear++] = t;
	while (front < rear)
	{
		int u = q[front++];
		mark[u] = 0;
		for (int i = head[u]; i != -1; i = E[i].next)
		{
			int v = E[i].v;
			if (E[i].f>0 && dis[v] > dis[u] + E[i].c)
			{
				dis[v] = dis[u] + E[i].c;
				pre[v] = u;
				if (!mark[v])
				{
					q[rear++] = v;
					mark[v] = 1;
					if (++cnt[v] > n + m + 2)
						return v;
				}
			}
		}
	}
	return -1;
}

void add_edge(int u, int v, int f, int c)
{
	E[tol].v = v;
	E[tol].f = f;
	E[tol].c = c;
	E[tol].next = head[u];
	head[u] = tol++;
}

void Build(int s, int t)
{
	memset(head, -1, sizeof head);
	tol = 0;
	for (int i = 1; i <= n; i++)
	{
		add_edge(s, i, 0, 0);
		add_edge(i, s, bg[i].c, 0);
	}
	for (int i = 1; i <= m; i++)
	{
		add_edge(i + n, t, home[i].c - sum[i], 0);
		add_edge(t, i + n, sum[i], 0);
	}
	for (int i = 1; i <= n; i++)
	for (int j = 1; j <= m; j++)
	{
		add_edge(i, j + n, oo - line[i][j], getCost(bg[i],home[j]));
		add_edge(j + n, i, line[i][j], -getCost(bg[i], home[j]));
	}
}

int main()
{
	int s, t;
	while (scanf("%d%d", &n, &m) != EOF)
	{
		s = n + m + 1;
		t = s + 1;
		for (int i = 1; i <= n; i++)
		{
			scanf("%d%d%d", &bg[i].x, &bg[i].y, &bg[i].c);
		}
		for (int i = 1; i <= m; i++)
		{
			scanf("%d%d%d", &home[i].x, &home[i].y, &home[i].c);
		}
		memset(sum, 0, sizeof sum);
		for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
		{
			scanf("%d", &line[i][j]);
			sum[j] += line[i][j];
		}
		Build(s, t);
		int ans = Spfa(s, t);
		if (ans == -1)
			printf("OPTIMAL\n");
		else
		{
			printf("SUBOPTIMAL\n");
			memset(mark, 0, sizeof mark);
			int a = ans, b;
			while (true)
			{
				if (!mark[a])
				{
					mark[a] = 1;
					a = pre[a];
				}
				else
				{
					b = a;
					break;
				}
			}
			do
			{
				int u = pre[a];
				int v = a;
				if (u <= n&&v > n)
					line[u][v - n]++;
				if (v <= n&&u > n)
					line[v][u - n]--;
				a = pre[a];
			} while (a != b);
			for (int i = 1; i <= n; i++)
			{
				for (int j = 1; j < m; j++)
				{
					printf("%d ", line[i][j]);
				}
				printf("%d\n", line[i][m]);
			}
		}
	}
	return 0;
}
/*
3 4
-3 3 5
-2 -2 6
2 2 5
-1 1 3
1 1 4
-2 -2 7
0 -1 3
3 1 1 0
0 0 6 0
0 3 0 2
3 4
-3 3 5
-2 -2 6
2 2 5
-1 1 3
1 1 4
-2 -2 7
0 -1 3
3 0 1 1
0 0 6 0
0 4 0 1

*/
memset(mark, 0, sizeof mark);
 int a = ans, b;
 while (true)
 {
 if (!mark[a])
 {
 mark[a] = 1;
 a = pre[a];
 }
 else
 {
 b = a;
 break;
 }
 }
 do
 {
 int u = pre[a];
 int v = a;
 if (u <= n&&v > n)
 line[u][v - n]++;
 if (v <= n&&u > n)
 line[v][u - n]--;
 a = pre[a];
 } while (a != b);


注意:返回的v不一定在环里面
这段代码是找出这个环,并且见环对应的变做变化,正向边++,反向边--


代码的构建参与网络图
void Build(int s, int t)
{
 memset(head, -1, sizeof head);
 tol = 0;
 for (int i = 1; i <= n; i++)
 {
 add_edge(s, i, 0, 0);
 add_edge(i, s, bg[i].c, 0);
 }
 for (int i = 1; i <= m; i++)
 {
 add_edge(i + n, t, home[i].c - sum[i], 0);
 add_edge(t, i + n, sum[i], 0);
 }
 for (int i = 1; i <= n; i++)
 for (int j = 1; j <= m; j++)
 {
 add_edge(i, j + n, oo - line[i][j], getCost(bg[i],home[j]));
 add_edge(j + n, i, line[i][j], -getCost(bg[i], home[j]));
 }
}
体会下。。。