UVA 563 Crimewave (Dinic)

题目大意

新克诺伦丹(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

*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Dinic算法是一种用来解决最大流问题的算法,它的核心思想是构建分层图和阻塞流量的概念。通过多次增广路径的查找来找出最大流。 在Python中实现Dinic算法可以参考以下步骤: 1. 首先,需要定义一个图的类,包括节点数和邻接表等属性,用来存储图的结构。 2. 实现图的构建函数,根据边的信息添加邻接表。 3. 基于Dinic算法,需要实现一个辅助函数来在网络中查找增广路径。可以使用广度优先搜索(BFS)或深度优先搜索(DFS)来实现。通过不断搜索增广路径,直到找不到增广路径为止。 4. 实现Dinic算法的主函数,其中包括初始化流量和残余网络,以及进行多次增广路径搜索的过程。 5. 在每一次增广路径搜索中,需要更新流量和残余网络,并计算每一条边的容量。 6. 最后,需要输出最大流的值。 以下是一种可能的实现方式: ```python class Graph: def __init__(self, nodes): self.nodes = nodes self.adjacency = [[] for _ in range(nodes)] def add_edge(self, u, v, capacity): self.adjacency[u].append([v, capacity, 0, len(self.adjacency[v])]) self.adjacency[v].append([u, 0, 0, len(self.adjacency[u]) - 1]) def bfs(self, start, end): # 使用BFS查找增广路径 level = [-1] * self.nodes level[start] = 0 queue = [start] while queue: current_node = queue.pop(0) for neighbor in self.adjacency[current_node]: if level[neighbor[0]] < 0 and neighbor[1] > neighbor[2]: # 更新节点的层级 level[neighbor[0]] = level[current_node] + 1 queue.append(neighbor[0]) return level[end] >= 0 def dfs(self, current_node, end, flow): # 使用DFS查找增广路径 if current_node == end: return flow while self.adjacency[current_node][current_edge[current_node]][1] <= self.adjacency[current_node][current_edge[current_node]][2]: current_edge[current_node] += 1 for i in range(current_edge[current_node], len(self.adjacency[current_node])): neighbor = self.adjacency[current_node][i] if level[neighbor[0]] == level[current_node] + 1 and neighbor[1] > neighbor[2]: min_flow = min(flow, neighbor[1] - neighbor[2]) temp_flow = self.dfs(neighbor[0], end, min_flow) if temp_flow > 0: neighbor[2] += temp_flow self.adjacency[neighbor[0]][neighbor[3]][2] -= temp_flow return temp_flow return 0 def dinic(self, start, end): level = [0] * self.nodes max_flow = 0 while self.bfs(start, end): current_edge = [0] * self.nodes while True: flow = self.dfs(start, end, float('inf')) if not flow: break max_flow += flow return max_flow g = Graph(4) g.add_edge(0, 1, 3) g.add_edge(0, 2, 2) g.add_edge(1, 2, 2) g.add_edge(1, 3, 1) g.add_edge(2, 3, 3) print("最大流为:", g.dinic(0, 3)) ``` 这个实现中,以一个简单的图作为例子进行了测试。首先,创建一个有4个节点的图,然后添加边和容量。最后,通过调用dinic函数来计算最大流的值,并将结果输出。 希望以上回答对你有所帮助,如果有任何问题需要进一步解答,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值