题意:给定一个最小费用流的模型,根据给定的数据判定是否为最优解,如果不为最优解则给出一个比给定更优的解即可。不需要得出最优解。
解法:由给定的数据能够得出一个残图,且这个图满足了最大流的性质,判定一个残图是否满足最小费用最大流的判定依据类比于最大流中的层次图的构建,此时只需要判定图中是否存在负环即可。在写的过程中由于图中的编号和抽象了之后的编号有一个变换关系,所以处理要小心,一开始直接写了个最小费用最大流,通过费用来判定是否为最优TLE。找负环的时候,该题宜从汇点开始遍历,因为源点发出的边肯定全部满流(由于要疏散所有的人),而市政大楼与避难所在内部又分别没有边相连,所以要形成回路就必定会经过汇点。spfa跳出来的点也不一定就是环上的点,需要自己找出环上的一个点,方法就是沿着一条路进行标记,如果标记到了已经标记的点,那么这个点就一定在环上。
具体残图构图:
市政大楼(b)到避难所(s)两两之间一定有边,这在原图中也是不改变的,费用为两点之间的曼哈顿距离加1;
如果b到s已经有流量,那么s到b也一定有负费用的流量,费用为两点之间的曼哈顿距离加1;
如果源点到b有流量,那么b到源点一定有负费用的流量,如果从源点发出的边没有满流,那么仍然有边到达b,费用为0;
如果s到汇点有流量,那么汇点到s一定有负费用的流量,如果汇集到汇点的边没有满流,那么仍然有边到达汇点,费用为0;
代码如下:
#include <cstdlib> #include <cstdio> #include <cstring> #include <iostream> #include <queue> #include <algorithm> using namespace std; const int INF = 0x3f3f3f3f; int N, M, SS, TT; int dis[205], vis[205], cnt[205]; int mp[205][205]; int way[205][205]; int path[205]; struct Point { int x, y, c; }b[105], s[105]; bool gao() { // 用来判定负圈的存在 int flag = false, pos; memset(dis, 0x3f, sizeof (dis)); memset(vis, 0, sizeof (vis)); memset(cnt, 0, sizeof (cnt)); queue<int>q; dis[TT] = 0, vis[TT] = 1, cnt[TT] = 1; q.push(TT); while (!q.empty()) { int u = q.front(); q.pop(); vis[u] = 0; for (int v = 0; v <= TT; ++v) { if (mp[u][v] != INF && dis[v] > dis[u] + mp[u][v]) { dis[v] = dis[u] + mp[u][v]; path[v] = u; if (!vis[v]) { vis[v] = 1, ++cnt[v]; if (cnt[v] > TT) { flag = true; pos = v; break; } q.push(v); } } } } if (flag) { puts("SUBOPTIMAL"); memset(vis, 0, sizeof (vis)); int u = path[pos], v; while (1) { if (!vis[u]) { vis[u] = 1; u = path[u]; } else break; } int rec = u; v = u, u = path[u]; while (1) { if (u <= N) { way[u][v-N]++; } else { way[v][u-N]--; } if (u == rec) break; v = u, u = path[u]; } for (int i = 1; i <= N; ++i) { for (int j = 1; j <= M; ++j) { printf(j == 1 ? "%d" : " %d", way[i][j]); } puts(""); } } else { puts("OPTIMAL"); } } int dist(int m, int n) { return abs(b[m].x - s[n].x) + abs(b[m].y - s[n].y) + 1; } int num1[105], num2[105]; int main() { while (scanf("%d %d", &N, &M) != EOF) { SS = 0, TT = N+M+1; memset(num1, 0, sizeof (num1)); memset(num2, 0, sizeof (num2)); memset(mp, 0x3f, sizeof (mp)); // 若j>N, mp[i][j]表示从i到j-N的距离 for (int i = 1; i <= N; ++i) { scanf("%d %d %d", &b[i].x, &b[i].y, &b[i].c); } for (int i = 1; i <= M; ++i) { scanf("%d %d %d", &s[i].x, &s[i].y, &s[i].c); } for (int i = 1; i <= N; ++i) { for (int j = 1; j <= M; ++j) { scanf("%d", &way[i][j]); mp[i][N+j] = dist(i, j); num1[i] += way[i][j]; num2[j] += way[i][j]; if (way[i][j]) { mp[N+j][i] = -dist(i, j); } } } for (int i = 1; i <= N; ++i) { if (num1[i] < b[i].c) mp[SS][i] = 0; if (num1[i]) mp[i][SS] = 0; } for (int i = 1; i <= M; ++i) { if (num2[i] < s[i].c) mp[N+i][TT] = 0; if (num2[i]) mp[TT][N+i] = 0; } gao(); } return 0; }