挺好的题 充分利用了spfa 判断最费用流是否最优的充分必要条件是——图中是否存在负环 如果存在说明最费用流最优否则相反
/*
* this code is made by LinMeiChen
* Problem:
* Type of Problem: 最小费用流
* Thinking: 先按照市政府的方发建图判断是否可行,如果不行这用自己的方法
* Feeling:
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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]));
}
}
体会下。。。