POJ2175 Evacuation Plan

嘟嘟嘟

题目大意:给一个费用流的残量网络,判断是不是最优解。如果不是,输出比当前解更优的任意一种方案。

<.br>

刚开始以为是水题:建完图后跑费用流,并记录选取方案,最后输出。

然而这样会\(TLE\)!

所以我还是看了题解。

原来用了费用流的一条性质:当前流是最小费用流 \(<=>\)残量网络中没有负圈。

所以做法就是建好残量网络,然后跑\(spfa\)找负环,然后修改环中的边的流量。

具体做法:

1.根据题目可知,从源点向每一栋楼房的边一定都流满了,因此可以不建。而且这也告诉我们要从汇点开始跑。

2.找负环的时候,要保证每一条边的残量都是正的,然后以费用为边权找负环。

3.找到负环退出的时候当前点可能不是负环。解决方法是沿着记录的路径往回跑,并标记,如果遇到一个已经被标记的点,那么这个点肯定在负环里。那么从这个点再开始找路径,直到又回到这个点,这期间的点一定都在负环里。

4.因为只用输出比当前更优的一种方案,因此把负环上的边流量加1即可。

5.这题用邻接矩阵是最方便的。因为按题中要求,几乎每两个点之间都会连边,所以几乎没有浪费空间,而且在后面的修改和统计答案的时候也非常方便。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("")
#define space putchar(‘ ‘)
#define Mem(a, x) memset(a, x, sizeof(a))
#define rg register
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 105;
inline ll read()
{
  ll ans = 0;
  char ch = getchar(), last = ‘ ‘;
  while(!isdigit(ch)) last = ch, ch = getchar();
  while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - ‘0‘, ch = getchar();
  if(last == ‘-‘) ans = -ans;
  return ans;
}
inline void write(ll x)
{
  if(x < 0) x = -x, putchar(‘-‘);
  if(x >= 10) write(x / 10);
  putchar(x % 10 + ‘0‘);
}

int n, m, s, t;
struct Buil
{
  int x, y, num;
}a[maxn], b[maxn];

int G[maxn << 1][maxn << 1], c[maxn << 1][maxn << 1], sum[maxn];

bool in[maxn << 1];
int dis[maxn << 1], du[maxn << 1], pre[maxn << 1];
int spfa()
{
    Mem(in, 0); Mem(dis, 0x3f); Mem(du, 0);
    queue<int> q; q.push(t);
    in[t] = 1; dis[t] = 0; du[t] = 1;
    while(!q.empty())
    {
        int now = q.front(); q.pop(); in[now] = 0;
        for(int i = 0; i <= t; ++i)
        {
            if(G[now][i] && dis[i] > dis[now] + c[now][i])
            {
                dis[i] = dis[now] + c[now][i];
                pre[i] = now;
                if(!in[i])
                {
                    in[i] = 1, q.push(i);
                    if(++du[i] > t) return i;
                }
            }
        }
    }
    return -1;
}

int Dis(Buil a, Buil b)
{
    return abs(a.x - b.x) + abs(a.y - b.y) + 1;
}

int main()
{
    n = read(); m = read(); s = 0; t = n + m + 1;
    for(int i = 1; i <= n; ++i) a[i].x = read(), a[i].y = read(), a[i].num = read();
    for(int i = 1; i <= m; ++i) b[i].x = read(), b[i].y = read(), b[i].num = read();
    for(int i = 1; i <= n; ++i)
    {
        for(int j = 1; j <= m; ++j)
        {
            c[i][j + n] = Dis(a[i], b[j]);
            c[j + n][i] = -c[i][j + n];
            G[i][j + n] = a[i].num;
        }
    }
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= m; ++j)
        {
            int d = read();
            G[i][j + n] -= d;
            G[j + n][i] = d;
            sum[j] += d;
        }
    for(int i = 1; i <= m; ++i)
    {
        G[i + n][t] = b[i].num - sum[i];
        G[t][i + n] = sum[i];
    }
    int ans = spfa();
    if(ans == -1) puts("OPTIMAL");
    else
    {
        puts("SUBOPTIMAL");
        Mem(in, 0);
        int x = ans;
        while(!in[x])
        {
            in[x] = 1;
            x = pre[x];
        }
        ans = x;
        do
        {
            G[pre[x]][x]--;
            G[x][pre[x]]++;
            x = pre[x];
        }while(x != ans);
        for(int i = 1; i <= n; ++i)
        {
            for(int j = 1; j <= m; ++j) write(G[j + n][i]), space;
            enter;
        }
    }
    return 0;
}

再发一个用费用流写的\(TLE\)的代码的关键部分:

建图:

  for(int i = 1; i <= n; ++i)
    {
      a[i].x = read(); a[i].y = read(); a[i].num = read();
      addEdge(s, i, a[i].num, 0);
    }
  for(int i = 1; i <= m; ++i)
    {
      b[i].x = read(); b[i].y = read(); b[i].num = read();
      for(int j = 1; j <= n; ++j) addEdge(j, i + n, INF, qdis(a[j], b[i]));
      addEdge(i + n, t, b[i].num, 0);
    }

更新:

void update()
{
  int x = t;
  while(x != s)
    {
      int i = pre[x];
      e[i].cap -= flow[t];
      e[i ^ 1].cap += flow[t];
      Ans[e[i].from][e[i].to] += flow[t];
      Ans[e[i].to][e[i].from] -= flow[t];
      x = e[i].from;
    }
  minCost += flow[t] * dis[t];
}

原文地址:https://www.cnblogs.com/mrclr/p/10014089.html

时间: 2024-11-10 15:01:32

POJ2175 Evacuation Plan的相关文章

解题报告 之 POJ2175 Evacuation Plan

解题报告 之 POJ2175 Evacuation Plan Description The City has a number of municipal buildings and a number of fallout shelters that were build specially to hide municipal workers in case of a nuclear war. Each fallout shelter has a limited capacity in term

POJ 2175 Evacuation Plan 费用流 负圈定理

题目给了一个满足最大流的残量网络,判断是否费用最小. 如果残量网络中存在费用负圈,那么不是最优,在这个圈上增广,增广1的流量就行了. 1.SPFA中某个点入队超过n次,说明存在负环,但是这个点不一定在负环上. 2.这个负环可能包括汇点t,所以构建残量网络的时候也要考虑防空洞到t上的容量. //#pragma comment(linker, "/STACK:1024000000,1024000000") #include<cstdio> #include<cstring

Codeforces Gym 100002 E &quot;Evacuation Plan&quot; 费用流

"Evacuation Plan" Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100002 Description The City has a number of municipal buildings and a number of fallout shelters that were build specially to hide municipal workers in case 

POJ 2175 Evacuation Plan (费用流,负环,消圈法,SPFA)

http://poj.org/problem?id=2175 Evacuation Plan Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 3256   Accepted: 855   Special Judge Description The City has a number of municipal buildings and a number of fallout shelters that were build

POJ 2175 Evacuation Plan

Evacuation Plan Time Limit: 1000ms Memory Limit: 65536KB This problem will be judged on PKU. Original ID: 217564-bit integer IO format: %lld      Java class name: Main The City has a number of municipal buildings and a number of fallout shelters that

ZOJ 1553 Evacuation Plan

最小费用最大流..... 建图: 源点 到 每栋楼  连容量为B,花费为0 的边 每个避难所 到 汇点  连容量为C,花费为0 的边 楼 到 避难所 连容量INF,花费 曼哈顿距离+1 的边 跑费用流后比较.... POJ 2175时限只有一秒.....会超时 Evacuation Plan Time Limit: 10000MS   Memory Limit: 32768KB   64bit IO Format: %lld & %llu [Submit]   [Go Back]   [Stat

POJ 2175 Evacuation Plan 费用流消圈

题目大意:给出一个费用流的模型和已经流过的一些边,问是否存在比这个解更优的解. 思路:直接用原图做一次费用流求最优解会T掉.先介绍费用流消圈定理:如果当前费用流的残量网络中存在负圈,那么当前流不是最优的解. 其实很好理解,结合原图和流过流量之后的反边,若出现了负圈,那么就可以沿着这个负圈增广,而且费用更小. 不过为了解决这个题我们并不需要建立完整的网络流,只需要建立残量网络之后SPFA看是否能找到负环即可. 具体建立方法: 如果一个避难地点有值,那么T向这个避难地点连边,费用0 若当前避难地点没

POJ - 2175 Evacuation Plan (最小费用流消圈)

题意:有N栋楼,每栋楼有\(val_i\)个人要避难,现在有M个避难所,每个避难所的容量为\(cap_i\),每个人从楼i到避难所j的话费是两者的曼哈顿距离.现在给出解决方案,问这个解决方案是否是花费最小的,若不是,则给出比这个更优的解. 分析:若只是要我们求一个最优解的话就用费用流做.现在要求判断是否最优,那么就是当前这张图中是否最短路还能被更新. 首先需要根据给定的解决方案重现这个状态下的残余网,其实只需要加入必要的弧即可:对与任意的楼与避难所(i,j),建边,费用为其距离;若i->j有流量

POJ.2175.Evacuation Plan(消圈)

POJ \(Description\) \(n\)个建筑物,每个建筑物里有\(a_i\)个人:\(m\)个避难所,每个避难所可以容纳\(b_i\)个人. 给出每个建筑物及避难所的坐标,任意两点间的距离为它们的曼哈顿距离\(+1\). 现在给出一个分配方案(\(g[i][j]\)表示第\(i\)个建筑物去第\(j\)个避难所的人数),问是否存在所有人移动的距离之和比当前更小的方案.如果存在,输出任意一组更小的方案. \(n,m\leq100\) \(Solution\) 直接跑费用流会T,但是也没