挺好的题 充分利用了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]));
}
}
体会下。。。