典型算法与ACM题目解析(1)—寻找最大流的标号法

 
典型算法与ACM题目解析(1)—寻找最大流的标号法
 
这种算法又叫Ford-Fulkerson算法,算法的核心思想是使用标号的方法不断寻找一个图上的可增广路径并且进行调整,直到找不到可增广路径为止,此时得到的可行流即是该网络的最大流。
 
算法导论上对这种算法的伪码表示如下
 
    FORD-FULKERSON(G, s, t)
    1 for each edge (u, v) E[G]
    2   do f[u, v] ← 0
    3       f[v, u] ← 0
    4 while there exists a path p from s to t in the residual network Gf
    5   do cf(p) ← min {cf(u, v) : (u, v) is in p}
    6       for each edge (u, v) in p
    7           do f[u, v] ← f[u, v] + cf(p)
    8               f[v, u] ← -f[u, v]
 
Google Code Jam 2006提供的第一道练习题所用的算法就是以上所说的算法
 
题目的大意是,给一个无向图和图上任意两点之间是否有通路,一个人从0点到1点总共最多有多少条不同的道路可选,要求一个点(0,1除外)最多只能有一条道路覆盖,要求最多有多少条满足这个条件的从0点到1点的道路。
 
由于题目给出的数据量太小,总点数只有12,估计搜索或枚举之类的方法应该可以通过,但是这道题最好的方法还是上面所说的求最大流的FF算法,如何将这个图转化成我们求最大流的图呢?我们常用的方法是拆点法。
 
将0,1之外的其它点全部都拆成两个,Xa和Xb,设到点某点Y有一条到X的路径,就设Yb->Xa的流量为1,同时设置Xa->Xb的流量为1,那么从X点流进的流量可能大于1,流出的流量也可能大于1,但是流经X点的流量最大为1,这也就保证了只有一条路经过X。
 
这个算法在平均情况下是O(V^3)的,在数据量如此之小的情况下不存在超时的可能性,当然除非程序出现了死循环。
 
代码如下:
 
PS:由于TopCoder只要求参赛者写出一个类和一个方法即可,所以此题的代码和ACM的代码有很大的不同,最重要的一点就是编译后不会生成.exe文件,毕竟程序里面没有main函数嘛!
 
/********************************************/
/*      Google Code Jam practice 1          */
/*      Algo:max-flow                       */
/*      Author:Sempr                        */
/********************************************/
 
#include <vector>
#include <string>
#include <cstring>
#define SIZE 30
using namespace std;
 
class SalesRouting
{
    int Q[SIZE];
    int Qf[SIZE];
    int c[SIZE][SIZE];
    int f[SIZE][SIZE];
    int M, N;
    int maxflow(int s, int t)
    {
        int head, tail, i;
        int h;
        int flow = 0;
        memset(f, 0, sizeof(f));
        while (1)
        {
            head = 0;
            tail = 1;
            Q[0] = s;
            memset(Qf, -1, sizeof(Qf));
            Qf[s] = 0;
            while (head < tail)
            {
                h = Q[head++];
                for (i = 0; i < N; i++)
                {
                    if (-1 == Qf[i] && c[h][i] > f[h][i])
                    {
                        Q[tail++] = i;
                        Qf[i] = h;
                    }
                }
                if (Qf[t] != -1)
                    break;
            }
            if (-1 == Qf[t])
                break;
            h = t;
            while (h != s)
            {
                f[Qf[h]][h]++;
                f[h][Qf[h]] = -f[Qf[h]][h];
                h = Qf[h];
            }
            flow++;
        }
        return flow;
    }
    public:
    int howMany(vector <string> adj)
    {
        int i, j;
        M = adj.size();
        N = (M - 1) * 2;
        memset(c, 0, sizeof(c));
        for (i = 2; i < M; i++)
        {
            c[i * 2 - 2][i * 2 - 1] = 1;
            for (j = 2; j < M; j++)
            {
                if (adj[i][j] == '1')
                {
                    c[i * 2 - 1][j * 2 - 2] = 1;
                }
            }
        }
        for (i = 0; i < M; i++)
        {
            if (adj[i][0] == '1')
                c[0][i * 2 - 2] = 1;
            if (adj[1][i] == '1')
                c[i * 2 - 1][1] = 1;
        }
        return maxflow(0, 1);
    }
};
 
 
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ACM竞赛是一项编程竞赛,常用算法与数据结构是竞赛选手在解决问题时所经常使用的方法和工具。以下是一些常见的算法和数据结构: 常用算法: 1.贪心算法:根据每一步的局部最优解来构建整体最优解。 2.动态规划:将问题划分为若干子问题,通过求解子问题的最优解来求得原问题的最优解。 3.分治法:将问题分解为若干个规模更小的子问题,分别求解这些子问题,然后合并子问题的解来得到原问题的解。 4.搜索算法:包括深度优先搜索(DFS)和广度优先搜索(BFS)等,用来穷举解空间以找到最优解。 5.图算法:包括最短路径算法(如Dijkstra和Floyd-Warshall算法)、最小生成树算法(如Prim和Kruskal算法)等,用于处理图相关的问题。 常用数据结构: 1.数组:一组相同类型的元素的集合,可以通过下标访问元素。 2.链表:一组由节点组成的数据结构,每个节点包含指向下一个节点的指针。 3.栈:一种先进后出(LIFO)的数据结构,只能在尾部进行插入和删除操作。 4.队列:一种先进先出(FIFO)的数据结构,可以在尾部插入元素,在头部删除元素。 5.堆:一种可以快速找到最大(或最小)元素的数据结构,分为大顶堆和小顶堆。 6.树:一种用来模拟具有层次结构的数据结构,包括二叉树、AVL树、红黑树等。 7.图:由节点和边组成的数据结构,用于表示各种复杂的关系或网络。 以上仅是ACM竞赛常用的一些算法和数据结构,选手在解决问题时还需要根据具体情况选择合适的算法和数据结构。掌握这些常用算法和数据结构,并根据实际问题进行灵活运用,可以提高解题效率和竞赛表现。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值