POJ 3308: Paratroopers

碰到一道非常讨厌的网络流。

因为精度的问题,

多路增广的dinic会WA。

只好仿照SPFA增广求费用流的写法来写了一个感觉像单路增广dinic的东西


题目链接:http://poj.org/problem?id=3308


题意:

一个n*m的网格上有一些点。

消去某一行上的点有一个代价,

消去某一列上的点也有一个代价,

这些代价都是实数且不小于1.0。

所需的总代价是这些代价的乘积。

问消去所有点需花费的最小代价。


算法 :

很经典的最小割模型。

取对数化乘为加。


代码如下:

#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstdlib>
#include<cstring>
#include<string>
#include<climits>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#include<stack>
#include<set>
#include<map>
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;

const int MAXN=200;
const int MAXM=100000;

int head[MAXN],Q[MAXN],p[MAXN];
bool vis[MAXN];
int to[MAXM],nxt[MAXM];
double cap[MAXM];
int E;

void _addedge(int u, int v, double w) {
    nxt[E]=head[u];
    to[E]=v;
    cap[E]=w;
    head[u]=E++;
}

void addedge(int u, int v, double w) {
    _addedge(u,v,w);
    _addedge(v,u,0.0);
}

bool bfs(int S, int T, int n) {
    memset(vis,0,sizeof(vis));
    vis[T]=true;
    int front=0,rear=0;
    Q[rear++]=T;
    while(front!=rear&&!vis[S]) {
        int u=Q[front++];
        for(int i=head[u]; i!=-1; i=nxt[i]) {
            if(cap[i^1]<eps) {
                continue;
            }
            int v=to[i];
            if(!vis[v]) {
                p[v]=i;
                vis[v]=true;
                Q[rear++]=v;
            }
        }
    }
    return vis[S];
}

double dfs(int S, int T, int n, double lim) {
    double tmp=INT_MAX;
    for(int u=S; u!=T; u=to[p[u]^1]) {
        tmp=min(tmp,cap[p[u]^1]);
        if(tmp<eps) {
            return 0.0;
        }
    }
    for(int u=S; u!=T; u=to[p[u]^1]) {
        cap[p[u]^1]-=tmp;
        cap[p[u]]+=tmp;
    }
    return tmp;
}

double dinic(int S, int T, int n) {
    double ans=0.0;
    while(bfs(S,T,n)) {
        ans+=dfs(S,T,n,INT_MAX);
    }
    return ans;
}

int main() {
    int cas;
    scanf("%d",&cas);
    while(cas--) {
        int n,m,k;
        scanf("%d%d%d",&n,&m,&k);
        int S=0,T=n+m+1;
        memset(head,-1,sizeof(head));
        E=0;
        for(int i=0; i<n; i++) {
            double cap;
            scanf("%lf",&cap);
            addedge(S,i+1,log(cap));
        }
        for(int i=0; i<m; i++) {
            double cap;
            scanf("%lf",&cap);
            addedge(i+n+1,T,log(cap));
        }
        while(k--) {
            int x,y;
            scanf("%d%d",&x,&y);
            x--;
            y--;
            addedge(x+1,y+n+1,INT_MAX);
        }
        printf("%.4f\n",exp(dinic(S,T,n+m+2)));
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值