题目描述:
有n个建筑物,m个避难所,告诉你每个建筑物的坐标和拥有的人。告诉你每个避难所的坐标和容量。建筑物i和避难所j的距离为|xi-xj| + |yi-yj| + 1。现在要求把建筑物的人都疏散到避难所。给你一个方案,n行m列,i行j列表示建筑物i有x[i][j]的人去j避难所。
问你这个方案是不是最优的,不是的话,输出一个比给定方案好的方案即可(SPJ)。
解题报告:
如果直接求最小费用流求出最优值输出,算法正确,但是会TLE,注意到题目不一定要求输出最优,好一点就可以。
“可行流x为最小费用流的充要条件是残量网络中不存在负费用增广圈”
按照这个条件,我们建立残量网络即可,由于要判断负圈,所以只要剩余容量大于0,就连接边即可,走几遍都无所谓。
为了简化,可以不需要原来的源点(因为需要源点满流,所以不会沿着建筑物点走回源点当然建立了也无所谓)。
省下的就是残量网络建图:
所有的建筑物i和避难所j,连接ij,边权为正的距离。
如果原方案i到j有人,连接ji,边权为负的距离。
如果j避难所的人数大于0,连接汇点和j,边权0.
如果j避难所没有满,连接j和汇点,边权0.
个人理解如下:
1 : 因为汇点包含在负圈里, 所以连接汇点和j。
2 : 未满,所以可以增广调整,而增广则需与汇点相连。
这样,在残量网络中,容量大于0的边就都建立出来了。
从汇点出发,找负圈,如果找到了,按照负圈的边,依次更改方案即可。
比如负圈中有建筑物到i到避难所j的点,x[i][j]++
如果有避难所j到建筑物i的点,x[i][j]—
输出方案即可。
代码如下:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
#define maxn 122
#define maxv 333
#define INF 1e9
int N,M;
int V;
int X[maxn],Y[maxn],B[maxn];
int P[maxn],Q[maxn],C[maxn];
int E[maxn][maxn];
int g[maxv][maxv];
int prev[maxv][maxv];
bool used[maxv];
void read()
{
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]);
}
void solve()
{
V = N + M + 1;
for(int i = 0 ; i < V ; ++i){fill(g[i] , g[i] + V , INF);}
for(int j = 0 ; j < M ; ++j){
int sum = 0 ;
for(int i = 0 ; i < N ; ++i){
int c = abs(X[i] - P[j]) + abs(Y[i] - Q[j]) + 1;
g[i][N+j] = c;
if( E[i][j] > 0 )g[N+j][i] = -c;//因为只需要增流一次
sum += E[i][j];
}
if( sum > 0 )g[N+M][N+j] = 0;
if( sum < C[j] )g[N+j][N+M] = 0;
}
for(int i = 0 ; i < V ; ++i){
for(int j = 0 ; j < V ; ++j){
prev[i][j] = i;
}
}
for(int k = 0 ; k < V ; ++k){
for(int i = 0 ; i < V ; ++i){
for(int j = 0 ; j < V ; ++j){
if(g[i][j] > g[i][k] + g[k][j]){
g[i][j] = g[i][k] + g[k][j];
prev[i][j] = prev[k][j];//类似链
if( i == j && g[i][i] < 0 ){//形成环
fill(used,used + V,false);
for(int v = i ; !used[v]; v = prev[i][v]){
used[v] = true;
if( v >= N )
E[prev[i][v]][v-N]++;
else E[v][prev[i][v] - N]--;
}
printf("SUBOPTIMAL\n");
for(int x = 0 ; x < N ; ++x){
for(int y = 0 ; y < M ; ++y){
printf("%d%c",E[x][y],y+1 == M ?'\n':' ');
}
}
return ;
}
}
}
}
}
printf("OPTIMAL\n");
}
int main()
{
read();
solve();
}