zoj 2874 & poj 3308 Paratroopers (最小割)

题意:

一个m*n大小的网格,已知伞兵着陆的具体位置(行和列)。现在在某行(或某列)

安装一架激光枪,一架激光枪能杀死该行(或该列)所有的伞兵。在第i行安装一架

激光枪的费用是Ri,在第i列安装的费用是Ci。要安装整个激光枪系统,总费用为这些

激光枪费用的乘积。

求杀死所有伞兵的最小费用。

构图:

把伞兵视为边,行与列视为顶点。增加源点和汇点,对于第i行,从源点向顶点i连接一条

容量为Ri的边。对于第j列,从顶点j向汇点连接一条容量为Rj的边。

如果某一点(i,j)有伞兵降落,则从顶点Ri向顶点Cj连接一条容量为无穷大的边。

算法:

根据割的性质,源点和汇点必不连通,则割边必定在S->R,R->C,C->T其一。为了求得最小容量,

将R->C设为无穷大,则其不可能被选中。这样割边集为S-->R,C-->T的集合,也就是选中行或列。

此时求得的最小割为花费最小的方案。

由于花费为行和列的乘积,则通过对数运算把乘法转化为加法。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
#define maxm 15000
#define maxn 105
#define eps 1e-6
using namespace std;

struct node
{
    int v,next;
    double val;
}e[maxm<<1];
int st,en,n,m,l,cnt;
int d[maxn];
int head[maxn],cur[maxn];
const double INF = 1000007;
queue<int> q;

void init()
{
    st = 0,en = n+m+1;
    memset(head,-1,sizeof(head));
    cnt = 0;
}
void add(int x,int y,double z)
{
    e[cnt].v = y;
    e[cnt].val = z;
    e[cnt].next = head[x];
    head[x]=cnt++;
    e[cnt].v = x;
    e[cnt].val = 0;
    e[cnt].next = head[y];
    head[y]=cnt++;
}
bool bfs()
{
    while(!q.empty())
        q.pop();
    memset(d,-1,sizeof(d));
    int u;
    d[st] = 0;
    q.push(st);
    while(!q.empty())
    {
        u = q.front();
        q.pop();
        for(int i=head[u];i!=-1;i=e[i].next)
        {
            int t = e[i].v;
            if(e[i].val>0 && d[t]==-1)
            {
                d[t] = d[u]+1;
                q.push(t);
                if(t==en) return true;
            }
        }
    }
    return false;
}

double dfs(int x,double flow)
{
    if(x==en || fabs(flow)<=eps) return flow;
    double ret = 0,dd;
    for(int& i=cur[x];i!=-1;i=e[i].next)
    {
        int t = e[i].v;
        if(d[t] == d[x]+1 && (dd = dfs(t,min(flow,e[i].val)))>0)
        {
            e[i].val-=dd;
            e[i^1].val+=dd;
            flow-=dd;
            ret+=dd;
            if (fabs(flow) <= eps) break;
        }
    }
    return ret;
}
double Dinic()
{
    double tmp = 0,maxflow = 0;
    while(bfs())
    {
        for(int i=0;i<=en;i++)
            cur[i] = head[i];
        maxflow+=dfs(st,INF);
    }
    return maxflow;
}

int main()
{
    int T,a,b;
    double x;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&m,&n,&l);
        init();
        for(int i=1;i<=m;i++)
        {
            scanf("%lf",&x);
            add(st,i,log(x));
        }
        for(int i=1;i<=n;i++)
        {
            scanf("%lf",&x);
            add(i+m,en,log(x));
        }
        for(int i=1;i<=l;i++)
        {
            scanf("%d%d",&a,&b);
            add(a,b+m,INF);
        }
        printf("%.4f\n",exp(Dinic()));
    }
    return 0;
}

zoj 2874 & poj 3308 Paratroopers (最小割)

时间: 2024-11-11 05:14:44

zoj 2874 & poj 3308 Paratroopers (最小割)的相关文章

poj 3308 Paratroopers 最小割 最小点权覆盖

题目链接:http://poj.org/problem?id=3308 题意: 有一个M*N的图,上面的一些点上有伞兵. 可以设置一些枪在每行或者每列上,通过射击,这行或这列的伞兵就会被消灭.每个枪的设置有一个花费,如果设置多个枪,那么花费是设置每个枪的乘积. 问消灭所有伞兵最少的花费是多少. 思路: 每个点的伞兵至少要用那一列或者那一行设置的枪去消灭,那么就可以应用点覆盖的模型.把伞兵看成是一条边,这条边至少要用一个点来覆盖. 而题目中最终花费是所有花费的乘积,那么可以用对数log(x)+lo

POJ 3308 Paratroopers 最小点权覆盖 求最小割

不懂这个建模是什么原理,以后把二分图相关的东西看完再补上把= = #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <climits> #include <string> #include <iostream> #include <map> #include <cstdlib> #i

POJ 3308 Paratroopers (二分图最小点权覆盖 -&gt; 最小割 -&gt; 最大流)

POJ 3308 Paratroopers 链接:http://poj.org/problem?id=3308 题意:有一个N*M的方阵,有L个伞兵降落在方阵上.现在要将所有的伞兵都消灭掉,可以在每行每列装一个高射炮,如果在某行(某列)装上高射炮之后,能够消灭所有落在该行(该列)的伞兵.每行每列安高射炮有费用,问如何安装能够使得费用之积最小. 思路:首先题目要求乘积最小,将乘积对e取对数,会发现就变成了求和.然后抽象出一个二分图,每一行是x部的一个点,每个点有权值,权值为费用取ln.每一列是y部

ZOJ 2587 Unique Attack 判断最小割是否唯一

很裸的判断最小割是否唯一.判断方法是先做一遍最大流求最小割,然后从源点和汇点分别遍历所有能够到达的点,看是否覆盖了所有的点,如果覆盖了所有的点,那就是唯一的,否则就是不唯一的. #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <climits> #include <string> #include <iostr

POJ 1815 Friendship(最小割)

http://poj.org/problem?id=1815 Friendship Time Limit: 2000MS   Memory Limit: 20000K Total Submissions: 9026   Accepted: 2534 Description In modern society, each person has his own friends. Since all the people are very busy, they communicate with eac

zoj 3792 Romantic Value(最小割下边数最小)

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5300 大致题意:给出一个无向图,以及起点与终点.要删除一些边使得起点与终点不连通,在删掉边的权值之和最小的情况下要求删除的边数尽量少.求出一个比值:剩余边数权值和/删除的边数. 思路:删除边的权值之和最小显然是求最小割即最大流.但同时要求删除边数最少,解决方法是把边数也加到权值上去,一起求最大流,因为边数最多是1000,即每条边的边权置为 w*10000+1,1代表这一条边.

poj 1815 Friendship 最小割输出最小方案

这题卡了好久啊,最小割模型很容易想,拆点就行.就像poj的Thieves一样 每个点 a拆成 a->a',容量为1. 其他相连的点 a'->b ,容量为INF 源连接s',t连接汇 问题在于输出最小的割集 更好的方法我还不会,只能枚举. 这里基于贪心的思想,从小到大删边, 若删除i->i',会使得最小割变小,则输出i,并且i->i'这条边不要恢复 若最小割不变,则恢复这条边,继续枚举. 一开始就是因为恢复了要割去的边,无限WA. #include<cstdio> #in

poj 1815 Friendship (最小割+拆点+枚举)

题意: 就在一个给定的无向图中至少应该去掉几个顶点才能使得s和t不联通. 算法: 如果s和t直接相连输出no answer. 把每个点拆成两个点v和v'',这两个点之间连一条权值为1的边(残余容量) v和v''分别是一个流进的点,一个流出的点. 根据求最小割的性质,权值小的边是可能被选择的(断开的). 添加源点st=0和汇点en=2*n+1,源点与s连权值为inf的边,t''与汇点连权值为inf的边. s与s'',t与t''连权值为inf的边,这样保证自己和自己是不会失去联系的. 如果i和j有边

ZOJ 2587 Unique Attack(最小割唯一性判断)

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2587 题意:判断最小割是否唯一. 思路: 最小割唯一性的判断是先跑一遍最大流,然后在残留网络中分别从源点和汇点出发dfs,只有当该边还有流量可用时可以访问下一个顶点,最后如果所有顶点都访问了,那么就是唯一的,否则不唯一. 接下来图解一下: 先看下面这个容量均为1的图: 跑一遍最大流后的残留网络如下(只画正向弧): 接下来从源点和汇点出发都无法访问任何顶点,因为剩余流量皆为