网络流(最小割最大流)【POJ3308】

【POJ3308】

  题意:简单的说就是有一个方阵,告诉你方阵里的一些位置有一些敌人伞兵,而且你有一些激光枪,这些激光枪有穿透效果,也就是如果摆在第一排的排头话就可以杀死这一排的所有伞兵,当然也可以放在一列的列头可以杀死一列的伞兵。在不同行或列建立激光枪所需的费用不一样,建立这些激光枪的总费用是建立每个激光枪的乘积,求建立激光枪杀死所有伞兵的总费用。输入:第一行是测试数据T, 第二行是行数(r) 列数(c)  伞兵数(l),第三行是建立在i行的激光枪花费的费用,第四行是建立在i列所花费的费用,接着有l行是伞兵的坐标。

  解题思路:该题依然是一个最小割最大流问题,首先就是建图,我们可以把伞兵视为边,行和列视为顶点,再增加一个源点和一个汇点,源点连接每个行,每个列连接汇点,容量分别为在该行和该列建立激光枪的费用,伞兵的坐标视为边,容量为无穷大!根据割的性质,源点和汇点必不连通,因此割边必定存在s->r  r->c  c->t中,将r->c的容量设为无穷大,则不能被选中。这样割边就在s->r  c->t的集合,也就选中了对应的行和列,此时求得的最小割既为答案!    但是改题求得是乘积而不是和,那么问题来了,怎么将乘积变为和计算,那么下面直接附上代码,看完代码应该就明白了

#include<stdio.h>
#include<math.h>
int main()
{
    int x, y;
   while(~scanf("%d %d", &x, &y))
    printf("x和y的积为log(x)+log(y)=%lf\n", (double)exp((double) log(x)+(double)log(y)));
}

下面是ac代码:(值得注意的是如果用C++交的话是下面的代码, 如果用G++交的话就把最后printf里的“lf”的l去掉);

#define MAX 110
#define INF 10000000
#include<string.h>
#include<algorithm>
#include<math.h>
#include<stdio.h>
struct Node
{
    double c, f;
}map[MAX][MAX];
int pre[MAX];  //pre[i]为增广路径顶点i前一个顶点的序号
int queue[MAX]; //数组模拟队列
int s, t; //源点,汇点;
bool BFS() //BFS求增广路
{
    int i, cur, qs, qe; //队列当前结点;队列头,队列尾
    memset(pre, -1, sizeof(pre));
    pre[s] = s;
    qs = 0;
    qe = 1;
    queue[qs] = s;
    while(qs < qe)
    {
        cur = queue[qs++];
        for( i = 0; i <= t; i++)
        {
            if(pre[i] == -1 && map[cur][i].c-map[cur][i].f > 0)
            {
                queue[qe++] = i;
                pre[i] = cur;
                if(i == t) return 1;//汇点不在层次网络中
            }
        }
    }
    return 0; //汇点不在层次网络中
}
double maxflow() //求最大流
{
    double max_flow = 0, min;
    int i;
    while(BFS())
    {
        min = INF;
        for(i = t; i != s; i = pre[i]) //调整网络
        {
            if( map[pre[i]][i].c - map[pre[i]][i].f < min)
                min = map[pre[i]][i].c - map[pre[i]][i].f;
        }
        for( i = t; i != s; i = pre[i])
        {
            map[pre[i]][i].f += min;
            map[i][pre[i]].f -= min;
        }
        max_flow += min;
    }
    return max_flow; //返回最大流
}
int main()
{
    int i, n, m, l, r, cc, w;
    double c;
    scanf("%d", &w);
    while(w--)
    {
        memset(map, 0, sizeof(map));
        scanf("%d%d%d", &n, &m, &l);
        s = 0;
        t = n+m+1;
        构建网络;用对数运算将乘法转化为加法
        for( i = 1; i <= n; i++)
        {
            scanf("%lf", &c);
            map[s][i].c = log(c);
        }
        for( i = 1; i <= m; i++)
        {
            scanf("%lf", &c);
            map[i+n][t].c = log(c);

        }
        for(i = 1; i <= l; i++)
        {
            scanf("%d%d", &r, &cc);
            map[r][n+cc].c = 10000000;
        }
        printf("%.4lf\n", exp(maxflow())); //输出时将对数值转换为原值
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值