题目大意:给出一个费用流的模型和已经流过的一些边,问是否存在比这个解更优的解。
思路:直接用原图做一次费用流求最优解会T掉。先介绍费用流消圈定理:如果当前费用流的残量网络中存在负圈,那么当前流不是最优的解。
其实很好理解,结合原图和流过流量之后的反边,若出现了负圈,那么就可以沿着这个负圈增广,而且费用更小。
不过为了解决这个题我们并不需要建立完整的网络流,只需要建立残量网络之后SPFA看是否能找到负环即可。
具体建立方法:
如果一个避难地点有值,那么T向这个避难地点连边,费用0
若当前避难地点没有满,那么向T连边,费用0
若一个位置到一个避难地点有原流量,那么为了能让他流回去,避难地点向那个位置连边,费用曼哈顿距离
所有的位置向所有的避难地点连边,费用曼哈顿距离
找圈什么的随便yy一下就好了- -
CODE:
#define _CRT_SECURE_NO_WARNINGS
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 210
#define MAXE 100010
#define T (MAX - 1)
using namespace std;
struct Point{
int x,y;
int cnt;
void Read() {
scanf("%d%d%d",&x,&y,&cnt);
}
}start[MAX],_end[MAX];
inline int Calc(const Point &p1,const Point &p2)
{
return abs(p1.x - p2.x) + abs(p1.y - p2.y);
}
int m,n;
int map[MAX][MAX];
int now[MAX];
int head[MAX << 1],total;
int _next[MAXE],s[MAXE],aim[MAXE],length[MAXE];
void Add(int x,int y,int z)
{
_next[++total] = head[x];
s[total] = x;
aim[total] = y;
length[total] = z;
head[x] = total;
}
int t[MAX];
int f[MAX],from[MAX];
bool v[MAX];
void FindCircle(int x)
{
puts("SUBOPTIMAL");
memset(v,false,sizeof(v));
int now = x;
while(!v[now]) {
v[now] = true;
now = from[now];
}
int src = now;
do {
if(from[now] <= m && now != T)
++map[from[now]][now - m];
if(from[now] != T && now <= m)
--map[now][from[now] - m];
now = from[now];
}while(now != src);
for(int i = 1; i <= m; ++i)
for(int j = 1; j <= n; ++j)
printf("%d%c",map[i][j]," \n"[j == n]);
}
bool SPFA()
{
static queue<int> q;
memset(f,0x3f,sizeof(f));
f[T] = 0;
q.push(T);
while(!q.empty()) {
int x = q.front(); q.pop();
v[x] = false;
for(int i = head[x]; i; i = _next[i])
if(f[aim[i]] > f[x] + length[i]) {
f[aim[i]] = f[x] + length[i];
from[aim[i]] = x;
if(!v[aim[i]]) {
v[aim[i]] = true;
q.push(aim[i]);
if(++t[aim[i]] > (m + n + 1)) {
FindCircle(aim[i]);
return true;
}
}
}
}
return false;
}
int main()
{
cin >> m >> n;
for(int i = 1; i <= m; ++i)
start[i].Read();
for(int i = 1; i <= n; ++i)
_end[i].Read();
for(int i = 1; i <= m; ++i)
for(int j = 1; j <= n; ++j)
Add(i,j + m,Calc(start[i],_end[j]));
for(int i = 1; i <= m; ++i)
for(int j = 1; j <= n; ++j) {
scanf("%d",&map[i][j]);
now[j] += map[i][j];
if(map[i][j]) Add(j + m,i,-Calc(start[i],_end[j]));
}
for(int i = 1; i <= n; ++i) {
if(now[i]) Add(T,i + m,0);
if(now[i] < _end[i].cnt) Add(i + m,T,0);
}
if(!SPFA()) puts("OPTIMAL");
return 0;
}