这题我真是呵呵了,交了13发才AC。
不过期间也学会了不少东西,例如论读懂题意的重要性,求负环的正确姿势。
题意:
有n个建筑,m个避难所。每个建筑有一定人数,每个避难所所容纳的人数有限。
现在已经有了一个逃离方案。让你判断是否是最优,如果不是,让你找出更优的。
思路:
一开始以为直接求最优了事,结果直接TLE了事。
根据已有的逃离方案建好图,然后开始判断图中是否存在负环。
不存在负环则说明已经是最优。
存在负环则说明还可以更优。(还是不太懂为何存在负环就不是最优,铭神是这么说就这样么写了。的确按正常情况来跑算法是不会出现负环的)
存在负环则开始找负环。只要找出其中一个负环,流量更新1点也行,只要比当前的方案优秀。
建图:
根据给出方案构造残余网络;
从s流出的流量肯定是满的,但避难所可能还可以容纳更多的人。因此可以忽略s点。
*求负环:
遍历一遍每个避难所,从避难所出发,每次所走过的点都标记下来,并且保存路径。沿路更新权值时其实用了bellmanFord的思想,不过采用dfs的方式。
当找到了一个已经走到过的点,则说明找到了负环。记下当前这个点,表示结束点。
因为找负环可能出现一个负环多一条尾巴的情况,不记录下结束点,根据路径往回更新流量就会出错。(如下图)
*每次从新的避难所出发时,不需要把上一次的费用初始化,会TLE掉。不初始化仍然正确,因为每个点所保存的权值(小于等于0)。如果你从新的避难所出发,仍然无法使那点的权值小于上一次寻找的所得的权值,是没必要从那点继续走下去,因为根本无法找到负环,要是找的到,上一次的寻找已经找到了。初始化反而重复走无法找到负环的路。
AC代码:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <queue>
using namespace std;
#define debug cout<<"??"<<endl;
const int MAXN = 205;
const int MAXM = 105;
const int MAXEDGE = 1e5 + 5;
const int INF = 0x3f3f3f3f;
int path[MAXN][MAXM];
int n, m;
struct CEdge
{
int from, to, cap, flow, cost, next;
}edge[MAXEDGE];
struct CMCMF
{
int s, t, pp;
int head[MAXN], d[MAXN], p[MAXN];
bool vis[MAXN];
CMCMF(int ss, int tt)
{
s = ss, t = tt;
pp = cot = 0;
memset(head, -1, sizeof(head));
memset(vis, false, sizeof(vis));
memset(d, 0 ,sizeof(d));
}
void addEdge(int u, int v, int cap, int flow, int cost)
{
edge[pp] = (CEdge){u, v, cap, flow, cost, head[u]};
head[u] = pp++;
edge[pp] = (CEdge){v, u, 0, -flow, -cost, head[v]};
head[v] = pp++;
}
void getPath()
{
for(int i = 1;i <= n; i++)
{
int next = head[i];
while(next != -1)
{
CEdge &e = edge[next];
if(e.cap > 0)
{
path[i][e.to-n] = e.flow;
}
next = e.next;
}
}
}
int cot, stp;
void update()
{
for(int i = cot-1; i >= 0; i--)
{
edge[p[i]].flow += 1;
edge[p[i]^1].flow -= 1;
if(edge[p[i]].from == stp) break;
}
}
int findNegativeRing(int u, int t, int c)
{
if(vis[u])
{
stp = u;
cot = c;
return t;
}
vis[u] = true;
int f = 0, next = head[u];
while(next != -1)
{
CEdge &e = edge[next];
if(e.cap > e.flow && d[e.to] > d[u] + e.cost)
{
d[e.to] = d[u] + e.cost;
p[c] = next;
f = findNegativeRing(e.to, min(t, e.cap - e.flow), c + 1);
if(f != 0) break;
}
next = e.next;
}
vis[u] = false;
return f;
}
};
struct Build
{
int x, y, num;
}b[MAXN], s[MAXM];
int sum[MAXN];
int main()
{
while(scanf("%d%d", &n ,&m) != EOF)
{
//init
int ss = 0, t = n+m+1;
CMCMF mcmf(ss, t);
for(int i = 1;i <= n; i++)
scanf("%d%d%d", &b[i].x, &b[i].y, &b[i].num);
for(int i = 1;i <= m; i++)
scanf("%d%d%d", &s[i].x, &s[i].y, &s[i].num);
//build graph
int tmp, dis;
memset(sum, 0 ,sizeof(sum));
for(int i = 1;i <= n; i++)
{
for(int j = 1;j <= m; j++)
{
dis = abs(s[j].x - b[i].x) + abs(s[j].y - b[i].y) + 1;
scanf("%d", &tmp);
sum[j] += tmp;
mcmf.addEdge(i, j+n, INF, tmp, dis);
}
}
for(int i = n+1;i <= n+m; i++)
mcmf.addEdge(i, t, s[i-n].num, sum[i-n], 0);
//findNegativeRing
bool mark = false;
for(int i = n+1;i <= n+m; i++)
{
//memset(mcmf.d, INF, sizeof(mcmf.d));
//mcmf.d[i] = 0;
mcmf.cot = 0;
int f = mcmf.findNegativeRing(i, INF ,0);
if(f != 0)
{
mcmf.update();
mark = true;
break;
}
}
//output
if(!mark)
puts("OPTIMAL");
else
{
puts("SUBOPTIMAL");
mcmf.getPath();
for(int i = 1;i <= n; i++)
{
for(int j = 1;j <= m; j++)
{
printf("%d%c", path[i][j], j == m ? '\n':' ');
}
}
}
}
return 0;
}