您的位置:首页 > 理论基础 > 计算机网络

UVa 563 Crimewave (网络流构图+最大流+挺好的+双向边)

2015-08-08 21:47 525 查看


题意:有一个n × m的网点状街区,在这个n × m的街区上,有l家银行。有一伙劫匪要去抢银行,抢完以后要跑,他们逃跑的路线不能重叠,逃出n × m的街区边界才算是逃跑成功。给出街区大小和各银行位置,问是否可以全部成功逃脱。



参考:http://www.cnblogs.com/scau20110726/archive/2012/12/20/2827177.html

算法思路:对于给定的网格,行为S列为A,我们按行优先给所有点标号,从1到S*A。然后对每个点拆点,拆点后一个点变为两个点(那么总点数为2*S*A),在这里我把拆点后的两个点叫做“前点”和“后点”,对于坐标为(i,j)的点,拆点后“前点”的编号为u=(i-1)*A+j , “后点”的编号好v=u+S*A;

我们还要额外设置一个源点s,编号为0,一个汇点,编号为2*S*A+1。从源点s建有向边指向所有的银行,从所有网格边沿的点建有向边指向汇点t,另外网格内一个点要和它上下左右四个点建立无向边(也就是两条有向边)。数据很大,要用邻接表保存



问题就是,我们已经事先拆点了,原来的两个点(i,j)和(i,j+1)有连线那么拆点后怎么链接呢?

第一部分(一个点和它上下左右的四个点建边):从这个点的“后点”和四周的点的“前点”建有向边(因为用邻接表建图,所以所有的有向边都有反边,注意反边的容量为0)。

第二部分(源点和所有银行建边):源点s和所有银行的“前点”建有向边(还有反边)

第三部分(所有网格边沿的点和汇点建边):所有网格边沿的点的“后点”和汇点建有向边(还有反边)

因此

第一部分:两个点a,b之间会有四边有向边,一条是a点的“后点”指向b点的“前点”,一条是b点的“后点”指向a点的“前点”。这两条还附带两条反边所以一共四条

第二部分:源点和银行的“前点”有边,再附带一个反边

第三部分:网格边沿点的“后点”和汇点有边,再附带一个反边

所有边的容量都1(这样就起到了每个点只能用一次的效果),附带的反边容量当然是为0

建图后,直接EK,算最大流,最大流等于银行个数那么可以逃脱,不等(小于)就不可以
Dinic算法:
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#include <queue>
#include <vector>
#include <set>
#include <climits>
#include <map>
#include <string>
using namespace std;
typedef long long ll;
int s, a, b, st, ed;
const int OF = 2500;        //一开始是用s*a做的
const int INF = 0x3f3f3f3f;
const int maxn = 5005;
const int FIN = 5001;       //汇点按理说s*a*2+1可以的,可这里卡BUG了
int dir[4][2] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};

struct Edge
{
    int from,to,cap,flow;
    Edge(int from=0,int to=0,int cap=0,int flow=0):from(from),to(to),cap(cap),flow(flow){}
};

vector<Edge> edges;
vector<int> G[maxn];
int vis[maxn], d[maxn];
int cur[maxn];

void AddEdge(int from,int to,int cap)
{
    edges.push_back(Edge(from,to,cap,0));
    edges.push_back(Edge(to,from,0,0));
    int m=edges.size();
    G[from].push_back(m-2);
    G[to].push_back(m-1);
}

int BFS()
{
    memset(vis, 0, sizeof(vis));
    queue<int> Q;
    Q.push(st);
    d[st] = 0;
    vis[st] = 1;
    while (!Q.empty())
    {
        int u = Q.front(); Q.pop();
        for (int i = 0; i < G[u].size(); i++)
        {
            Edge &e = edges[G[u][i]];
            if (!vis[e.to] && e.cap > e.flow)
            {
                vis[e.to] = 1;
                d[e.to] = d[u] + 1;
                Q.push(e.to);
            }
        }
    }
    return vis[ed];
}

int DFS(int u, int a)
{
    if (u == ed || a == 0) return a;
    int flow = 0, f;
    for (int &i = cur[u]; i < G[u].size(); i++)
    {
        Edge &e = edges[G[u][i]];
        if (d[u] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap - e.flow))) > 0)
        {
            e.flow += f;
            edges[G[u][i]^1].flow -= f;
            flow += f;
            a -= f;
            if (a == 0) break;
        }
    }
    return flow;
}

int Maxflow()
{
    int ans = 0;
    while (BFS())
    {
        memset(cur, 0, sizeof(cur));
        ans += DFS(st, INF);
    }
    return ans;
}

int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
        st = 0, ed = FIN;
        for (int i = 0; i <= FIN; i++) G[i].clear();
        edges.clear();
        scanf("%d %d %d", &s, &a, &b);
        for(int i=1;i<=s;i++)
            {
                for(int j=1;j<=a;j++)
                {
                    AddEdge((i-1)*a+j,(i-1)*a+j+OF,1);
                    for(int k=0;k<4;k++)
                    {
                        int x=i+dir[k][0];
                        int y=j+dir[k][1];
                        if((x<1)||(x>s)||(y<1)||(y>a))continue;
                        AddEdge((i-1)*a+j+OF,(x-1)*a+y,1);
                    }
                    if((i==1)||(i==s)||(j==1)||(j==a))AddEdge((i-1)*a+j+OF,FIN,1);
                }
            }
        for(int i=0;i<b;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            AddEdge(0,(x-1)*a+y,1);
        }
        int ans;
        ans = Maxflow();
        if (ans == b) printf("possible\n");
        else printf("not possible\n");
    }
    return 0;
}



EK算法:
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#include <queue>
#include <vector>
#include <set>
#include <climits>
#include <map>
#include <string>
using namespace std;
typedef long long ll;
int s, a, b, st, ed;
const int OF = 2500;
const int INF = 0x3f3f3f3f;
const int maxn = 5005;
const int FIN = 5001;
int dir[4][2] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};

struct Edge
{
    int from,to,cap,flow;
    Edge(int from=0,int to=0,int cap=0,int flow=0):from(from),to(to),cap(cap),flow(flow){}
};

vector<Edge> edges;
vector<int> G[maxn];
int vis[maxn];
int p[maxn];

void AddEdge(int from,int to,int cap)
{
    edges.push_back(Edge(from,to,cap,0));
    edges.push_back(Edge(to,from,0,0));
    int m=edges.size();
    G[from].push_back(m-2);
    G[to].push_back(m-1);
}

int EK()
{
    int f=0;
    for(;;)
    {
        memset(vis,0,sizeof(vis));
        vis[st]=INF;
        queue<int> q;
        q.push(st);
        while(!q.empty())
        {
            int u=q.front();
            q.pop();
            for(int v=0; v<G[u].size(); ++v)
                {
                	Edge& e=edges[G[u][v]];
                	if(!vis[e.to]&&e.flow<e.cap)
                	{
                    	vis[e.to]=min(vis[u],e.cap-e.flow);
                    	p[e.to]=G[u][v];
                    	q.push(e.to);
               		}
               	}
        }
        if(vis[ed]==0)  break;
        for(int u=ed; u!=st; u=edges[p[u]].from)
        {
            edges[p[u]^1].flow-=vis[ed];
            edges[p[u]].flow+=vis[ed];
        }
        f+=vis[ed];
    }
    return f;
}

int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
        st = 0, ed = FIN;
        for (int i = 0; i <= FIN; i++) G[i].clear();
        edges.clear();
        scanf("%d %d %d", &s, &a, &b);
        for(int i=1;i<=s;i++)
            {
                for(int j=1;j<=a;j++)
                {
                    AddEdge((i-1)*a+j,(i-1)*a+j+OF,1);
                    for(int k=0;k<4;k++)
                    {
                        int x=i+dir[k][0];
                        int y=j+dir[k][1];
                        if((x<1)||(x>s)||(y<1)||(y>a))continue;
                        AddEdge((i-1)*a+j+OF,(x-1)*a+y,1);
                    }
                    if((i==1)||(i==s)||(j==1)||(j==a))AddEdge((i-1)*a+j+OF,FIN,1);
                }
            }
        for(int i=0;i<b;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            AddEdge(0,(x-1)*a+y,1);
        }
        int ans;
        ans = EK();
        if (ans == b) printf("possible\n");
        else printf("not possible\n");
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: