poj 3498 March of the Penguins(满足点容量限制的网络流)

46 篇文章 0 订阅
March of the Penguins
Time Limit: 8000MS Memory Limit: 65536K
Total Submissions: 3948 Accepted: 1802

Description

Somewhere near the south pole, a number of penguins are standing on a number of ice floes. Being social animals, the penguins would like to get together, all on the same floe. The penguins do not want to get wet, so they have use their limited jump distance to get together by jumping from piece to piece. However, temperatures have been high lately, and the floes are showing cracks, and they get damaged further by the force needed to jump to another floe. Fortunately the penguins are real experts on cracking ice floes, and know exactly how many times a penguin can jump off each floe before it disintegrates and disappears. Landing on an ice floe does not damage it. You have to help the penguins find all floes where they can meet.

A sample layout of ice floes with 3 penguins on them.

Input

On the first line one positive number: the number of testcases, at most 100. After that per testcase:

  • One line with the integer N (1 ≤ N ≤ 100) and a floating-point number D (0 ≤ D ≤  100 000 ), denoting the number of ice pieces and the maximum distance a penguin can jump.

  • N lines, each line containing xiyini and mi, denoting for each ice piece its X and Y coordinate, the number of penguins on it and the maximum number of times a penguin can jump off this piece before it disappears ( −10 000  ≤ xiyi ≤  10 000 , 0 ≤ ni ≤ 10, 1 ≤ mi ≤ 200).

Output

Per testcase:

  • One line containing a space-separated list of 0-based indices of the pieces on which all penguins can meet. If no such piece exists, output a line with the single number −1.

Sample Input

2
5 3.5
1 1 1 1
2 3 0 1
3 5 1 1
5 1 1 1
5 4 0 1
3 1.1
-1 0 5 10
0 0 3 9
2 0 1 1

Sample Output

1 2 4
-1
 
题意:有n个冰块,有每个冰块上有ni个企鹅,给出距离d,企鹅可以在冰块间跳跃当且仅当两冰块距离小于等于d。而且每个冰块都有一个最大跳跃次数mi,表示这个冰块最多能使mi企鹅离开。现在要使所有企鹅都跳到同一个冰块中,给出每个冰块的坐标,ni和mi,求企鹅们可以跳到哪些冰块相聚。
思路:要满足每个点的容量限制,这时要拆点。枚举每个冰块作为汇点,构图如下:将每个点i拆成i和i+n,连一条i到i+n的边,容量为mi,然后虚拟一个源点,源点到每个点连一条边,容量为ni,点之间距离满足小于等于d也要连边,例如:点j和点k满足距离小于d,则建边add(j+n,k,INF),add(k+n,j,INF)
 
AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <cmath>
#include <cstdlib>
#define L(rt) (rt<<1)
#define R(rt) (rt<<1|1)
#define ll __int64
#define eps 1e-6

using namespace std;

const int INF = 1000000000;
const int maxn = 300;

struct Edge
{
    int u, v, cap, flow, next;
} et[maxn*maxn];
struct node
{
    double x, y;
    int ni, mi;
} p[maxn];
int pre[maxn], cur[maxn], dis[maxn],cnt[maxn], low[maxn],  eh[maxn];
double d;
int n, s, t, num;
void init()
{
    memset(eh, -1, sizeof(eh));
    num = 0;
}
void add(int u, int v, int cap, int flow)
{
    Edge e = {u, v, cap, flow, eh[u]};
    et[num] = e;
    eh[u] = num++;
}
void addedge(int u, int v, int cap)
{
    add(u, v, cap, 0);
    add(v, u, 0, 0);
}
double getdis(node a, node b)
{
    return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
int isap(int s, int t, int nv)
{
    int u, v, now, flow = 0;
    memset(cnt, 0, sizeof(cnt));
    memset(low, 0, sizeof(low));
    memset(dis, 0, sizeof(dis));
    for(u = 0; u <= nv; u++) cur[u] = eh[u];
    low[s] =INF, cnt[0] = nv, u = s;
    while(dis[s] < nv)
    {
        for(now = cur[u]; now != -1; now = et[now].next)
            if(et[now].cap - et[now].flow && dis[u] == dis[v = et[now].v] + 1) break;
        if(now != -1)
        {
            cur[u] = pre[v] = now;
            low[v] = min(et[now].cap - et[now].flow, low[u]);
            u = v;
            if(u == t)
            {
                for(; u != s; u = et[pre[u]].u)
                {
                    et[pre[u]].flow += low[t];
                    et[pre[u]^1].flow -= low[t];
                }
                flow += low[t];
                low[s] = INF;
            }
        }
        else
        {
            if(--cnt[dis[u]] == 0) break;
            dis[u] = nv, cur[u] = eh[u];
            for(now = eh[u]; now != -1; now = et[now].next)
                if(et[now].cap - et[now].flow && dis[u] > dis[et[now].v] + 1)
                    dis[u] = dis[et[now].v] + 1;
            cnt[dis[u]]++;
            if(u != s) u = et[pre[u]].u;
        }
    }
    return flow;
}
int main()
{
    int tt;
    scanf("%d", &tt);
    while(tt--)
    {
        scanf("%d%lf", &n, &d);
        int sum = 0;
        for(int i = 0; i < n; i++)
        {
            scanf("%lf%lf%d%d", &p[i].x, &p[i].y, &p[i].ni, &p[i].mi);
            sum += p[i].ni;
        }
        int ans = 0;
        for(int k = 0; k < n; k++)
        {
            init();
            s = 2 * n;
            t = k;
            for(int i = 0; i < n; i++)
            {
                addedge(s, i, p[i].ni);
                addedge(i, i + n, p[i].mi);
            }
            for(int i = 0; i < n; i++)
                for(int j = i + 1; j < n; j++)
                    if(getdis(p[i], p[j]) <= d)
                    {
                        addedge(i + n, j, INF);
                        addedge(j + n, i, INF);
                    }
            if(isap(s, t, 2 * n + 1) == sum)
            {
                ans++;
                printf("%d ", k);
            }
        }
        if(!ans) puts("-1");
        else puts("");
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值