题意
传送门 POJ 2175
题解
流量过大,最小费用流跪了。题意只要求答案优于当前方案即可,用 Floyd-Warshall 在增广路上求负圈,沿着负圈增广即可得到费用更小的流。实现上考虑到所提供方案城市人数安排一一对应,则源点到城市的边满流,求负圈时源点可不考虑在内。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
#define min(a,b) (((a) < (b)) ? (a) : (b))
#define max(a,b) (((a) > (b)) ? (a) : (b))
#define abs(x) ((x) < 0 ? -(x) : (x))
#define INF 0x3f3f3f3f
#define delta 0.85
#define eps 1e-5
#define PI 3.14159265358979323846
using namespace std;
#define MAX_N 100
#define MAX_M 100
int N, M;
int X[MAX_N], Y[MAX_N], B[MAX_N];
int P[MAX_N], Q[MAX_N], C[MAX_N];
int E[MAX_N][MAX_M];
#define MAX_V 202
int dist[MAX_V][MAX_V];
int prev[MAX_V][MAX_V];
bool used[MAX_V];
void solve(){
memset(dist, 0x3f, sizeof(dist));
int t = N + M, V = t + 1;
// 建立邻接矩阵,为所有增广路赋对应权值
for(int j = 0; j < M; j++){
int s = 0;
for(int i = 0; i < N; i++){
int d = abs(X[i] - P[j]) + abs(Y[i] - Q[j]) + 1;
s += E[i][j];
if(E[i][j] > 0) dist[N + j][i] = -d;
if(E[i][j] < min(B[i], C[j])) dist[i][N + j] = d;
}
if(s > 0) dist[t][N + j] = 0;
if(s < C[j]) dist[N + j][t] = 0;
}
for(int i = 0; i < V; i++){
for(int j = 0; j < V; j++){
prev[i][j] = i;
}
}
// Floyd-Warshall
for(int k = 0; k < V; k++){
for(int i = 0; i < V; i++){
for(int j = 0; j < V; j++){
if(dist[i][j] > dist[i][k] + dist[k][j]){
dist[i][j] = dist[i][k] + dist[k][j];
prev[i][j] = prev[k][j];
if(i == j && dist[i][j] < 0){
// 找到负圈
memset(used, 0, sizeof(used));
for(int v = i; !used[v]; v = prev[i][v]){
used[v] = 1;
if(v != t && prev[i][v] != t){
if(v >= N) ++E[prev[i][v]][v - N];
else --E[v][prev[i][v] - N];
}
}
printf("SUBOPTIMAL\n");
for(int i = 0; i < N; i++){
for(int j = 0; j < M; j++){
printf("%d%c", E[i][j], j == M - 1 ? '\n' : ' ');
}
}
return;
}
}
}
}
}
printf("OPTIMAL\n");
}
int main(){
while(~scanf("%d%d", &N, &M)){
for(int i = 0; i < N; i++) scanf("%d%d%d", X + i, Y + i, B + i);
for(int i = 0; i < M; i++) scanf("%d%d%d", P + i, Q + i, C + i);
for(int i = 0; i < N; i++){
for(int j = 0; j < M; j++){
scanf("%d", &E[i][j]);
}
}
solve();
}
return 0;
}