UVA 563 Crimewave (Dinic)

本文介绍了一种基于网络流算法解决罪犯如何规划不同银行抢劫后逃逸路径的问题。通过构建图模型并利用Dinic算法求解最大流,判断是否存在合法的逃逸方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

新克诺伦丹(Nieuw Knollendam)是一个繁华的城市,城市完全由纵向和横向的街道构成,且每个街道的交叉口都会存在一个银行。然而这不幸地吸引了很多犯罪,每天都会有很多不同的银行被抢。这不仅对银行是个问题,对罪犯也是个问题,因为他们抢完银行在被警察追得不亦乐乎时,很可能和另一个罪犯在交叉口撞到一起,从而导致逃跑的困难。
所以罪犯们决定对逃跑路径进行规划,使得每天的逃跑路径都不会使用相同的交叉口。本题试图给出这样的一个规划。

解题思路

网络流题目的难度主要在建图上。这里实质上相当于每个点都有容量,为了能套用一般的最大流算法,我们应将其拆成两点,之间用容量为1的边连接。对原图,我们应将所有交叉口看成点,所有的出口也看成点。这两者都要进行拆点。

将二维的点按(0, 0)、(0, 1)、(0, 2) …顺序编码,依次编成点0、1、2…
为了避免每次访问点都需要算出对应编码,使用一个二维数组存储每个点对应的编码。方便起见,将该矩阵中元素声明为struct,每个元素都是拆点前的一个点,struct里包含拆点后的左点l和右点r,并存储对应的值。方便起见,使用单独两个变量存储s和t的值。

拆点完毕之后是连边,需要连接如下几种类型的边:1. 每个左拆点向右拆点连接一条单向边;2. 每个交叉口的右拆点分别向上下左右四个方向的左拆点连接一条单向边;3. 源点向所有银行(银行仅仅是特殊的交叉口)的左拆点连接一条有向边;4. 所有出口的右拆点向汇点连接一条有向边。需要注意的是,这里连接的边虽然都是容量为1的单向边,但都需要再提供一条对应的容量为0的反向边,用于表示图的残留网络。

最后运行任意最大流算法即可。若流大小等于银行数则存在这样一个方案,否则不存在。

#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;

const int MAXS = 52; // maximum number of streets/avenues
const int MAXN = MAXS*MAXS*2; // maximum number of vertices
const int MAXM = (MAXS+2)*(MAXS+2)*2 + MAXS*MAXS*16 + MAXS*MAXS*2 + MAXS*8; // maximum number of edges

const int NIL = -1;
const int INF = 99999999;
int dir[][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};

struct vertex {
    int l, r; // indicate a vertex's left position and right position
};

struct edge {
    int u, v;
    int cap, flow;
    int next;
    edge() {}
    edge(int u, int v, int cap, int flow, int next) {
        this->u = u;
        this->v = v;
        this->cap = cap;
        this->flow = flow;
        this->next = next;
    }
};

int s, a, b;
edge G[MAXM];
int m; // indicate the number of edges
int head[MAXN];
int d[MAXN];
vertex H[MAXS][MAXS]; // a look-up table telling us the vertex number
int source, sink;

inline void add(int u, int v, int cap)
{
    G[m] = edge(u, v, cap, 0, head[u]);
    head[u] = m;
    m++;
}

void initH()
{
    int count = 0;
    for(int i=0; i<=s+1; i++)
        for(int j=0; j<=a+1; j++) {
            H[i][j].l = count;
            count++;
            H[i][j].r = count;
            count++;
        }
    source = 1;
    sink = (s+2)*(a+2)*2-2;
}

void initG()
{
    memset(head, NIL, sizeof(head));
    m = 0;
    // connect left vertex with right vertex = (s+2)*(a+2)*2 edges
    for(int i=0; i<=s+1; i++)
        for(int j=0; j<=a+1; j++) {
            add(H[i][j].l, H[i][j].r, 1);
            add(H[i][j].r, H[i][j].l, 0);
            // unidirectional edge shall have a backward edge with a capacity of 0
            // to maintain the property of residual network
        }

    // connect crossings with the crossings/exits around = s*a*4*4 edges
    for(int i=1; i<=s; i++)
        for(int j=1; j<=a; j++)
            for(int k=0; k<4; k++) {
                // connect a.right with b.left
                add(H[i][j].r, H[i+dir[k][0]][j+dir[k][1]].l, 1);
                add(H[i+dir[k][0]][j+dir[k][1]].l, H[i][j].r, 0);

                // connect b.right with a.left 
                add(H[i+dir[k][0]][j+dir[k][1]].r, H[i][j].l, 1);
                add(H[i][j].l, H[i+dir[k][0]][j+dir[k][1]].r, 0);
            }

    // connect source with left vertex of banks = b*2 edges
    for(int i=0; i<b; i++) {
        int x, y;
        cin >> x >> y;
        add(source, H[x][y].l, 1);
        add(H[x][y].l, source, 0);
    }

    // connect right vertex of exits with sink = a*4+s*4 edges
    for(int j=1; j<=a; j++) {
        add(H[0][j].r, sink, 1);
        add(sink, H[0][j].r, 0);
        add(H[s+1][j].r, sink, 1);
        add(sink, H[s+1][j].r, 0);
    }
    for(int i=1; i<=s; i++) {
        add(H[i][0].r, sink, 1);
        add(sink, H[i][0].r, 0);
        add(H[i][a+1].r, sink, 1);
        add(sink, H[i][a+1].r, 0);
    }
}

// Dinic algorithm

bool Dinic_BFS()
{
    bool vis[MAXN];
    memset(vis, 0, sizeof(vis));
    queue<int> Q;
    Q.push(source);
    d[source] = 0;
    vis[source] = true;
    while(!Q.empty()) {
        int u = Q.front(); Q.pop();
        for(int i=head[u]; i!=-1; i=G[i].next) {
            edge &e = G[i];
            if(!vis[e.v] && e.cap > e.flow) {
                vis[e.v] = true;
                d[e.v] = d[u] + 1;
                Q.push(e.v);
            }
        }
    }
    return vis[sink]; // not connected if sink is not visited
}

// f is the maximum possible flow from source to u
// if u == sink, then DFS will return the maximum flow from source to sink
int Dinic_DFS(int u, int f)
{
    if(u == sink || f == 0) return f;
    int result = 0;
    for(int i=head[u]; i!=-1 && result < f; i=G[i].next) {
        edge &e = G[i];
        int temp = min(f-result, e.cap-e.flow);
        // if v is deeper than u and there exists an augmenting path 
        if(d[u] == d[e.v]-1 && e.cap > e.flow) {
            temp = Dinic_DFS(e.v, temp);
            e.flow = e.flow + temp; // increase flow
            G[i^1].flow = G[i^1].flow - temp; // decrease flow of backward edge
            result = result + temp;
        }
    }
    if(result == 0) d[u] = -2;
    return result;
}

int Dinic()
{
    int flow = 0;
    while(Dinic_BFS())
        while(1) {
            int val = Dinic_DFS(source, INF);
            if(val == 0) break;
            flow += val;
        }

    return flow;
}

int main()
{
    int p;
    cin >> p;
    for(int i=0; i<p; i++) {
        cin >> s >> a >> b;
        initH();

        initG();
        if(Dinic() == b)
            cout << "possible" << endl;
        else
            cout << "not possible" << endl;

    }
    return 0;
}


/*
  0 1 2 3 4 5 6
4   e e e e e t  s = supersource
3 e c c c c c e  t = supersink
2 e c c c c c e  c = crossing
1 e c c c c c e  e = exit
0 s e e e e e 
a map of 3 streets and 5 avenues

the vertices in the map shall be divided accordingly:
   0  1  2  3  4  5  6  7  8  9 10 11 12 13
4       el er el er el er el er el er  t     s = supersource
3 el er cl cr cl cr cl cr cl cr cl cr el er  t = supersink
2 el er cl cr cl cr cl cr cl cr cl cr el er  c = crossing
1 el er cl cr cl cr cl cr cl cr cl cr el er  e = exit
0     s el er el er el er el er el er
all directional edges shall only go into l and go out from r

*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值