伞兵(Paratroopers)

伞兵(Paratroopers)

时间限制: 1 Sec  内存限制: 128 MB

题目描述

公元 2500 年,地球和火星之间爆发了一场战争。最近,地球军队指挥官获悉火星入侵者将派一些伞兵来摧毁地球的兵工厂,兵工厂是一个 m×n 大小的网格。他还获悉每个伞兵将着陆的具体位置(行和列)。由于火星的伞兵个个都很强壮、而且组织性强,只要有一个伞兵存活了,就能摧毁整个兵工厂。因此,地球军队必须在伞兵着陆后瞬间全部杀死他们。

为了完成这个任务,地球军队需要利用高科技激光枪。他们能在某行(或某列)安装一架激光枪,一架激光枪能杀死该行(或该列)所有的伞兵。在第 i 行安装一架激光枪的费用是 Ri,在第 i 列安装的费用是 Ci。要安装整个激光枪系统,以便能同时开火,总的费用为这些激光枪费用的乘积。现在,你的任务是选择能杀死所有伞兵的激光枪,并使得整个系统的费用最小。

输入

输入文件的第 1 行为整数 T,表示测试数据的数目,接下来有 T 个测试数据。每个测试数据的第 1 行为 3 个整数 m、n 和 L,1≤m≤50,1≤n≤50,1≤L≤500,分别表示网格的行和列、以及伞兵的数目;接下来一行为 m 个大于或等于 1.0 的实数,第 i 个实数表示 Ri;再接下来一行为 n 个大于或等于 1.0 的实数,第 i 个实数表示 Ci;最后 L 行,每行为两个整数,描述了每个伞兵的着陆位置。

输出

对每个测试数据,输出搭建整个激光枪系统的最小费用,精确到小数点后面 4 位有效数字。

样例输入

1
4 4 5
2.0 7.0 5.0 2.0
1.5 2.0 2.0 8.0
1 1
2 2
3 3
4 4
1 4

样例输出

16.0000

题解:

首先可以看出这是一道网络流的题目,至于怎么输出所选边的乘积,稍后再说,这里先讲一讲建图。

1.虚构源点和汇点。

2.源点和行连边,汇点和列连边。

3.根据士兵的位置将相对应的行和列连边,权值为正无穷大。

然后讲一讲怎么输出乘积。

1.a*b*c=e^(log(a*b*c)) ,又log(a*b*c)=log(a)+log(b)+log(c)

2.a*b*c最小则log(a)+log(b)+log(c)最小,也就是以log(a),log(b),log(c)为边的图的网络流的最小割。

3.最大流=最小割,于是乎就成了求log(a),log(b),log(c)为边的图的最大流。

4.当然最后答案要用exp()还原一下。

5.完美!!!

代码仅供参考:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<queue>
#include<stack>
#include<ctime>
#include<vector>
using namespace std;
int n,m,l,t;
struct node
{
    int next,to;
    double cap;
}edge[50001];
int size=1,head[501];
void putin(int from,int to,double cap)
{
    size++;
    edge[size].to=to;
    edge[size].cap=cap;
    edge[size].next=head[from];
    head[from]=size;
}
void in(int from,int to,double cap)
{
    putin(from,to,cap);
    putin(to,from,0);
}
int dist[501],numbs[501];
void bfs(int src,int des)
{
    int i;
    memset(dist,0,sizeof(dist));
    memset(numbs,0,sizeof(numbs));
    queue<int>mem;
    mem.push(des);
    dist[des]=0;numbs[0]++;
    while(!mem.empty())
    {
        int x=mem.front();mem.pop();
        for(i=head[x];i!=-1;i=edge[i].next)
        {
            int y=edge[i].to;
            if(edge[i].cap==0&&dist[y]==0&&y!=des)
            {
                dist[y]=dist[x]+1;
                numbs[dist[y]]++;
                mem.push(y);
            }
        }
    }
    return;
}
double dfs(int src,double flow,int des)
{
    if(src==des)return flow;
    int i,mindist=n+m+2;
    double low=0;
    for(i=head[src];i!=-1;i=edge[i].next)
    {
        int y=edge[i].to;
        if(edge[i].cap)
        {
            if(dist[y]==dist[src]-1)
            {
                double t=dfs(y,min(flow-low,edge[i].cap),des);
                edge[i].cap-=t;
                edge[i^1].cap+=t;
                low+=t;
                if(dist[src]>=n+m+2)return low;
                if(flow==low)break;
            }
            mindist=min(mindist,dist[y]+1);
        }
    }
    if(!low)
    {
        if(!(--numbs[dist[src]]))dist[n+m+2]=n+m+2;
        ++numbs[dist[src]=mindist];
    }
    return low;
}
double ISAP(int src,int des)
{
    double ans=0;
    bfs(src,des);
    while(dist[0]<n+m+2)ans+=dfs(src,2e8,des);
    return ans;
}
int main()
{
    int i,j;
    scanf("%d",&t);
    while(t--)
    {
        size=1;
        memset(head,-1,sizeof(head));
        scanf("%d%d%d",&n,&m,&l);
        for(i=1;i<=n;i++)
        {
            double q;
            scanf("%lf",&q);
            in(0,i,log(q));
        }
        for(i=1;i<=m;i++)
        {
            double q;
            scanf("%lf",&q);
            in(n+i,n+m+1,log(q));
        }
        for(i=1;i<=l;i++)
        {
            int from,to;
            scanf("%d%d",&from,&to);
            in(from,n+to,100000000);
        }
        double maxflow=ISAP(0,n+m+1);
        printf("%.4lf\n",exp(maxflow));
    }
    return 0;
}
时间: 2024-10-14 05:21:08

伞兵(Paratroopers)的相关文章

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

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

zoj 2874 &amp; poj 3308 Paratroopers (最小割)

题意: 一个m*n大小的网格,已知伞兵着陆的具体位置(行和列).现在在某行(或某列) 安装一架激光枪,一架激光枪能杀死该行(或该列)所有的伞兵.在第i行安装一架 激光枪的费用是Ri,在第i列安装的费用是Ci.要安装整个激光枪系统,总费用为这些 激光枪费用的乘积. 求杀死所有伞兵的最小费用. 构图: 把伞兵视为边,行与列视为顶点.增加源点和汇点,对于第i行,从源点向顶点i连接一条 容量为Ri的边.对于第j列,从顶点j向汇点连接一条容量为Rj的边. 如果某一点(i,j)有伞兵降落,则从顶点Ri向顶点

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

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

POJ3308 Paratroopers(网络流)(最小割)

Paratroopers Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 8443   Accepted: 2541 Description It is year 2500 A.D. and there is a terrible war between the forces of the Earth and the Mars. Recently, the commanders of the Earth are infor

POJ - 3308 Paratroopers(最大流)

1.这道题学了个单词,product 还有 乘积 的意思.. 题意就是在一个 m*n的矩阵中,放入L个敌军的伞兵,而我军要在伞兵落地的瞬间将其消灭.现在我军用一种激光枪组建一个防御系统,这种枪可以安装在一行(或者一列),并且安装在不同行(或者不同列)的费用是不一样的,枪的攻击范围是一行(或者一列).安装所有枪的费用是它们每个费用的“乘积”,现在求组建这个系统需要的最小费用. 2.与前面做的二分图的一道题有点相似(POJ - 3041 Asteroids(最小点覆盖数)).但是现在这道题在不同行(

poj3308 Paratroopers --- 最小点权覆盖-&gt;最小割

题目是一个很明显的二分图带权匹配模型, 添加源点到nx建边,ny到汇点建边,(nx,ny)=inf建边,求最小割既得最小点权覆盖. 在本题中由于求的是乘积,所以先全部取log转换为加法,最后再乘方回来. #include <iostream> #include <cstring> #include <string> #include <cstdio> #include <cmath> #include <algorithm> #inc

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

Paratroopers Time Limit: 1000ms Memory Limit: 65536KB This problem will be judged on PKU. Original ID: 330864-bit integer IO format: %lld      Java class name: Main It is year 2500 A.D. and there is a terrible war between the forces of the Earth and

洛谷 P1913 L国的战斗之伞兵

P1913 L国的战斗之伞兵 题目背景 L国即将与I国发动战争!! 题目描述 为了在敌国渗透作战,指挥官决定:派出伞兵前往敌国!然而敌国的风十分强烈,能让伞兵在同一高度不停转悠,直到被刮到一个无风区……(可怜的小兵) 输入输出格式 输入格式: 第一行:n.m两个正整数,表示敌国的大小. 以下n行,每行m个字符,“u”表示风向北吹:“d”表示风向南吹:“l”表示风向西吹:“r”表示风向东吹:“o”表示无风.(上北下南,左西右东) 输出格式: 一个数:表示有几个点可以放下伞兵. 输入输出样例 输入样