pku 3308 Paratroopers 最大流最小割

http://poj.org/problem?id=3308

题意:火星人要和地球人PK,地球人间谍搞到了一份情报:火星人要搞伞兵,登陆在地球一个row*col的地图上,而且知道伞兵的数量和每个伞兵要降落的格子。为了消灭敌人,可以在某一行或者某一列安置激光枪。每个激光枪可以瞬间消灭这一行(或者列)的敌人。

安装消灭第i行的激光枪消费是ri。

安装消灭第j行的激光枪消费是ci。

现在总部要你花费最小的费用,安装好足够的激光枪去消灭所有的火星人,问最小的花费是多少。

这里花费的定义有点不同:是每个激光器消费的乘积。

思路:最小割_最大流,把伞兵看成边,行列看成节点,转化为了带权二分图最小点覆盖。加入超级源点和超级汇点,源点和所有行节点相连(权值ri),所有列节点和汇点相连(权值ci),如果a行b列有敌人,则把节点a和节点b相连。则问题又可以转化求最小割。

因为对任一敌人<a,b>,必然有source-->a-->b-->sink,故路径上的三条边<source,a>, <a,b>, <b,sink>中至少有一条边在割中,我们把<a,b>的权值设置为无限大,则其不可能被选中。于是割边集中必然有<source,a>和<b,sink>中的至少一条,也即对应选择了相应的行或列,我们把这些边的权值设置为花费,则最小割即是总花费的最小方案。

 

最小割:对于图中的两个点(一般为源点和汇点)来说,如果把图中的一些边去掉,如果它们之间无法连通的话,则这些边组成的集合就叫为割了。如果这些边有权值,最小割就是指权值之和最小的一个割。

最大流最小割:应用于网络中,指总流量不超过链路可承载的最大值,且在每条子路径上取尽可能少的流量。对任意一个只有一个源点一个汇点的图来说,从源点到汇点的最大流等于最小割。

#include <cstdio>

#include <cstring>

#include <cmath>

#include <queue>

#define maxn 107

using namespace std;



const double inf = 99999999.0;

double c[maxn][maxn];

int level[maxn];

bool vt[maxn];

int n,m,l;

bool layer(int s,int e)

{

    int i;

    queue<int>q;

    q.push(s);

    memset(level,-1,sizeof(level));

    level[s] = 1;

    while (!q.empty())

    {

        int p = q.front(); q.pop();

        for (i = s; i <= e; ++i)

        {

            if (c[p][i] && level[i] == -1)

            {

                level[i] = level[p] + 1;

                if (i == e) return true;

                q.push(i);

            }

        }

    }

    return false;

}

double dinic(int s,int e)

{

    deque<int>q;

    int pos,i,vs,ve;

    double maxf = 0;

    while (layer(s,e))

    {

        memset(vt,false,sizeof(vt));

        q.push_back(s);

        vt[s] = true;

        while (!q.empty())

        {

            int p = q.back();

            if (p == e)

            {

                double min = inf;

                for (i = 1; i < q.size(); ++i)

                {

                    vs = q[i - 1];

                    ve = q[i];

                    if (c[vs][ve] > 0 && min > c[vs][ve])

                    {

                        min = c[vs][ve];

                        pos = vs;

                    }

                }

                maxf += min;

                for (i = 1; i < q.size(); ++i)

                {

                     vs = q[i - 1];

                     ve = q[i];

                    if (c[vs][ve] > 0)

                    {

                        c[vs][ve] -= min;

                        c[ve][vs] += min;

                    }

                }

                while (!q.empty() && q.back() != pos)

                {

                    vt[q.back()] = false;

                    q.pop_back();

                }

            }

            else

            {

                for (i = s; i <= e; ++i)

                {

                    if (c[p][i] > 0 && !vt[i] && level[i] == level[p] + 1)

                    {

                        vt[i] = true;

                        q.push_back(i);

                        break;

                    }

                }

                if (i > e) q.pop_back();

            }



        }

    }

    return maxf;

}

int main()

{

    int i,x,y,s,e,t;

    double val;

    scanf("%d",&t);

    while (t--)

    {

        memset(c,0,sizeof(c));

        scanf("%d%d%d",&n,&m,&l);

        s = 0; e = n + m + 1;

        for (i = 1; i <= n; ++i)

        {

            scanf("%lf",&val);

            c[s][i] = log(val);

        }

        for (i = 1; i <= m; ++i)

        {

            scanf("%lf",&val);

            c[i + n][e] = log(val);

        }

        for (i = 1; i <= l; ++i)

        {

            scanf("%d%d",&x,&y);

            c[x][y + n] = inf;

        }

        double ans = dinic(s,e);

        printf("%.4lf\n",exp(ans));

    }

    return 0;

}

  

你可能感兴趣的:(oop)