La (NWERC2007 Problem B )- March of the Penguins(最大流 拆点)

这道题终于又了结果,哈哈,真开心!

其实这题不难,就是有点麻烦

题意:有n个点,每个点上有ni只企鹅,每个点只能允许mi只企鹅跳离这个点;并且每只企鹅,一次最多能跳D这么长的距离;目的是让左右企鹅能在一个点聚会,那么问,哪些点能作为聚会的点,一次输出这些点,如果没有方案那么就是输出-1。(n<=100, 1<= mi <= 200, 0<=ni<= 10, D <=100000)输出包括点数,D,每个点坐标以及每个点的ni和mi值。

分析:

让所有企鹅在同一点聚会,那么显然是看流入是否等于流出,最大流问题;

每个点有流量mi,显然,拆点,容量mi,这里设原来的i点为2i,并且和s直接相连,拆出来的为2i+1,如果要跳离这个点,那么就要经过这个拆点得来的边,而要是跳上这个点,那就直接连到2i这个点,因为没有花费;

每个点,最开始很可能就有企鹅,那么就相当于流入量,需要超级源点s,使得s进入i点的流量为ni;

要注意的是,一个点可以有无数只企鹅跳上来,只要不从这个点离开,那么就不花费流量,也就是说,汇点,应该从和从s直接相连的点里面,一次枚举;

还有,对于原来的点来说,点和点之间是双向的,双向流量都为INF

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <algorithm>
using namespace std;
#define For(i, s, e) for( int i = s; i < e; ++i )
#define esp 1e-9
const int N = 220;
const int INF = 1000000;
int p[N], a[N], T, n,cap[N][N], flow[N][N], mi[N], ni[N];
double D, x[N], y[N];
int maxFlow( int s, int t ) {
    queue<int>q;
    memset(flow, 0, sizeof(flow));
    int f = 0;
    while ( 1 ) {
        memset(a, 0, sizeof(a));
        a[s] = INF;
        q.push(s);
        while ( !q.empty() ) {
            int u = q.front(); q.pop();
            For(v, 0, 2*n+1) {
                if( !a[v] && cap[u][v] > flow[u][v] ) {
                    a[v] = min( a[u], cap[u][v] - flow[u][v]);
                    p[v] = u;
                    q.push(v);
                }
            }
        }
        if ( a[t] == 0 ) break;
        for ( int u = t; u != s; u = p[u] ) {
            flow[p[u]][u] += a[t];
            flow[u][p[u]] -= a[t];
        }
        f += a[t];
    }
    return f;
}

int main() {
    scanf("%d", &T);
    while ( T-- ) {
        scanf("%d%lf", &n, &D);
        int sum = 0, f = 0;
        For(i, 0, n) {
            scanf("%lf%lf%d%d", &x[i], &y[i], &ni[i], &mi[i]);
            sum += ni[i];
        }
        memset( cap, 0, sizeof(cap));
        int s = 2*n, e = s+1;
        //将源点与每个点相连,然后容量为这个点上的企鹅的个数;将每个点拆为2i和2i+1,容量为mi[i]
        For(i, 0, n) {
            cap[s][2*i] = ni[i];
            cap[2*i][2*i+1] = mi[i];
        }
        //建图,将距离小于D的点连起来,容量为INF
        For(i, 0, n) 
           For(j, 0, n) {
              double tmp = sqrt( (x[i]-x[j])*(x[i]-x[j]) + (y[i]-y[j])*(y[i]-y[j]));
              if ( tmp - D <= esp ) cap[2*i+1][2*j] = INF;
           }
        For(i, 0, n) {
           int tmp = maxFlow( s, 2*i );
           //printf("tmp  %d\n", tmp);
           if ( tmp >= sum ) {
               f++;
               if ( f > 1 ) printf(" ");
               printf("%d", i);
           }
        }
        if ( f == 0 ) printf("-1");
        printf("\n");
    }
}



评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值