POJ-3308 Paratroopers

Posted on 2021-12-11 16:07  Capterlliar  阅读(11)  评论(0编辑  收藏  举报

题意:一个n*m(实际题目n和m相反但习惯了)的方阵中有l个点,每个激光器可以消灭一行或一列的点,给出在行和列边上架激光器的费用,求最小费用乘积,使得每个点都被消灭。

解:第一反应是最小费用最大流。。。但这题是最小点覆盖,所以应该是最小割。将每个点的点权与源点和汇点连边,点和点之间边权为inf,表示不能切这条边。最小割的应用是分类,将点或边分给源点或汇点,每切掉一条边付出相应代价。本题中将每条边分给行和列去消灭,最终拆成两部分。

这题求的是乘积,网络流处理加法比较方便,乘积化加取对数,poj不认log2,只能用log+exp。

有空可以研究一下网络流了,可能是期末之后?

代码:

#include <algorithm>
#include <stack>
#include <vector>
#include <stdio.h>
#include <queue>
#include <math.h>
#include <cmath>
using namespace std;
#define maxx 10005
#define maxn 1005
#define maxe 5000005
#define inf 0x3f3f3f3f
#define eps 0.00000001
int n,m,l;
double nn[maxn],mm[maxn];

struct edge{
    int u,v;
    double w;
    int nxt;
}e[maxe];
int cnt=0,head[maxx]={0};
int st,ed;
void add(int u,int v,double w){
    e[cnt].u=u;
    e[cnt].v=v;
    e[cnt].w=w;
    e[cnt].nxt=head[u];
    head[u]=cnt++;
}
int depth[maxx],now[maxx];
int bfs(){
    memset(depth,0x3f,sizeof(depth));
    queue<int> q;
    q.push(st);depth[st] = 0;now[st] = head[st];
    while(!q.empty()){
        int x = q.front();q.pop();
        for(int i=head[x];i!=-1;i=e[i].nxt){
            int y=e[i].v;
            if(e[i].w>eps&&depth[y]==inf){
                q.push(y);
                now[y]=head[y];
                depth[y]=depth[x]+1;
                if(y==ed)
                    return 1;
            }
        }
    }
    return 0;
}
double dfs(int x,double flow){
    if(x==ed)
        return flow;
    double ans = 0,k;
    int i;
    for(i=now[x];i!=-1&&abs(flow)>eps;i=e[i].nxt){
        now[x]=i;
        int y=e[i].v;
        if(e[i].w>eps&&(depth[y]==depth[x]+1)){
            k=dfs(y,min(flow,e[i].w));
            if(abs(k)<eps)    depth[y]=inf;
            e[i].w-=k;
            e[i^1].w+=k;
            ans+=k;
            flow-=k;
        }
    }
    return ans;
}
double dinic(){
    double ans=0;
    while(bfs())
        ans+=dfs(st,inf);
    return ans;
}
void init(){
    st=n+m+2;ed=st+1;
    memset(head,-1,sizeof head);
    cnt=0;
}
signed main() {
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&n,&m,&l);
        for(int i=1;i<=n;i++)
            scanf("%lf",&nn[i]);
        for(int i=1;i<=m;i++)
            scanf("%lf",&mm[i]);
        init();
        for(int i=1;i<=n;i++)
            nn[i]=log(nn[i]);
        for(int i=1;i<=m;i++)
            mm[i]=log(mm[i]);
        for(int i=0;i<l;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            add(x,y+n,inf);
            add(y+n,x,0);
        }
        for(int i=1;i<=n;i++){
            add(st,i,nn[i]);
            add(i,st,0);
        }
        for(int i=1;i<=m;i++){
            add(i+n,ed,mm[i]);
            add(ed,i+n,0);
        }
        double ans=dinic();
        printf("%.4lf\n",exp(ans));
    }
    return 0;
}
View Code