解题报告 之 POJ3498 March of the Penguins

解题报告 之 POJ3498 March of the Penguins


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个浮冰,每个浮冰上有一些企鹅。每只企鹅每从浮冰跳到另一块浮冰时,出发浮冰的厚度减1,当厚度减为0则不能再从该浮冰跳出。企鹅可以从所在浮冰跳到企鹅跳跃范围内的任意其他浮冰(跳跃范围给出)。第i个浮冰上的坐标为xi,yi,有pen只企鹅,厚度为jump。问所有企鹅可以在哪些点相聚,如果不能相聚输出-1。


分析:一开始觉得样例是错的,后来才发现输出的时候浮冰从0开始编号,是很坑的一点。总体方法是用最大流枚举汇点,将可能的汇点输出即可。重点在在于建图,首先需要拆点,不然因为跑最大流时此消彼长的特性会出错。拆点边的负载为浮冰厚度jump,然后超级源点与每一拆点的入点相连,负载为浮冰企鹅数,然后能够跳的浮冰连边,负载为厚度jump。最后一个我最大流没理解透的地方被坑了:就是跑最大流的时候应该以拆点的入点为destination,想想为什么?首先因为入点是作为入的,所以有其合理性。第二是因为如果选出点为des,那么其输入最多只有入点的负载,根本就不够企鹅的数量。第三是一个疑问,如果选入点的话,那么入点本身的企鹅数不是不会被统计吗?答案是肯定的,因为入点可以先流到出点,根据此消彼长的原理,又会流回来。。。


上代码:

#include<iostream>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;

const int MAXN = 300;
const int MAXM = 30000;
const int INF = 0x3f3f3f3f;

struct Edge
{
	int to, cap, next;
};

struct ICE
{
	double x, y;
	int pen;
	double jump;
};

double dist(ICE i1,ICE i2)
{
	return sqrt((i1.x -i2.x )*(i1.x - i2.x) + (i1.y - i2.y)*(i1.y - i2.y));
}


Edge edge[MAXM];
ICE ice[MAXN];
int level[MAXN];
int head[MAXN];
int src, des, cnt;
double pendis;


void addedge(int from, int to, int cap)
{
	//edge[cnt].from = from;
	edge[cnt].to = to;
	edge[cnt].cap = cap;
	edge[cnt].next = head[from];
	head[from] = cnt++;

	//edge[cnt].from = to;
	edge[cnt].to = from;
	edge[cnt].cap = 0;
	edge[cnt].next = head[to];
	head[to] = cnt++;
}


int bfs()
{
	memset(level, -1, sizeof level);
	queue<int> q;
	while (!q.empty())
		q.pop();

	level[src] = 0;
	q.push(src);

	while (!q.empty())
	{
		int u = q.front();
		q.pop();

		for (int i = head[u]; i != -1; i = edge[i].next)
		{
			int v = edge[i].to;
			if (edge[i].cap > 0 && level[v] == -1)
			{
				level[v] = level[u] + 1;
				q.push(v);
			}
		}
	}
	return level[des] != -1;
}


int dfs(int u, int f)
{
	if (u == des)
		return f;
	int tem;
	for (int i = head[u]; i != -1; i = edge[i].next)
	{
		int v = edge[i].to;
		if (edge[i].cap > 0 && level[v] > level[u])
		{
			tem = dfs(v, min(f,edge[i].cap));
			if (tem > 0)
			{
				edge[i].cap -= tem;
				edge[i^1].cap += tem;
				return tem;
			}
		}
	}
	level[u] = -1;
	return 0;
}


int Dinic()
{
	int ans = 0, tem;
	while (bfs())
	{
		while (tem = dfs(src, INF))
			ans += tem;
	}
	return ans;
}


int main()
{
	int kase;
	cin >> kase;
	while (kase--)
	{
		int n; 
		int amount = 0;
		scanf("%d%lf", &n, &pendis);
		for (int i = 1; i <= n; i++)
		{
			scanf("%lf%lf%d%lf", &ice[i].x, &ice[i].y, &ice[i].pen, &ice[i].jump);
			amount += ice[i].pen;
		}
		int src = 0;
		int flag = false;
		for (int i = 1; i <= n; i++)
		{
			des = i;
			memset(head, -1, sizeof head);
			cnt = 0;

			for (int i = 1; i <= n; i++)
			{
				addedge(i, i + n, ice[i].jump);
			}

			for (int j = 1; j <= n; j++)
			{
				for (int k = 1; k <= n; k++)
				{
					if (dist(ice[j], ice[k]) <= pendis&&j != k)
						addedge(j + n, k, ice[j].jump);
				}
			}
			for (int k = 1; k <= n; k++)
			{
				if (ice[k].pen)
					addedge(src, k, ice[k].pen);
			}
			if (Dinic() == amount)
			{
				printf("%d ", i - 1);
				flag = true;
			}
		}
		if (!flag) printf("-1");
		printf("\n");
	}
	return 0;
}

英语竞赛还是不负恩泽的,只能以此为蓝桥杯的借口啦虽然还是很不甘心。




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值