解题报告 之 SGU438 The Glorious Karlutka River

解题报告 之 SGU438 The Glorious Karlutka River


Description



A group of  Mtourists are walking along the Karlutka river. They want to cross the river, but they couldn't find a bridge. Fortunately, there are some piles of rubbish floating in the water, and the  tourists have decided to try to cross the river by jumping from one pile to another. 

tourist can move up to  D meters in any direction at one jump. One jump takes exactly one second. tourists know that the river is  W meters wide, and they have estimated the coordinates of rubbish piles ( X iY i) and the capacity of each pile (  C i, the maximum number of  tourists that this pile can hold at the same time). Rubbish piles are not very large and can be represented as points. The river flows along the X axis.  tourists start on the river bank at 0 by  Y axis. The  Y coordinate of the opposite bank is  W

tourists would like to know if they can get to the opposite bank of the river, and how long it will take.

Input

First line of input consists of four integers: number of rubbish piles  N (0 ≤  N ≤ 50), number of  tourists M (0 <  M ≤ 50), maximum length of  tourist's jump  D (0 ≤  D ≤ 1000), and width of the river  W (0 <  W≤ 1000) Following  N lines describe the rubbish piles, each line consists of three integers: (0 <  X i < 1000, 0 <  Y i <  W, 0 ≤  C i ≤ 1000) — pile coordinates and capacity.

Output

Output a single number indicating the minimal time (in seconds) in which all  tourists will be able to cross the river, or the line "  IMPOSSIBLE" if it is impossible to cross the river.

Sample Input

sample input
sample output
3 10 3 7
0 2 2
4 2 2
2 4 3
6

sample input
sample output
3 10 3 8
0 2 2
4 2 2
2 4 3
IMPOSSIBLE


题目大意:有m个人要过河,他们现在丛x轴上某点出发(即河的南岸),河的北岸在y=w这条直线上。然后河中有n个石头,给出你石头坐标以及每个石头分别能同时承受多少个人。另外人能够跳的最长距离为d。问你是否所有人都能够过河?如果能的话,所花的最短时间是多少?


分析:不得不承认这道题我没做起。。。于是学习了一下原理再自己走了一遍,只能说谈谈我的理解,不保证正确。首先我们感觉到这个题用一个网络图是无法解决的。因为他无法解决时间问题,因为其流量是无限制的(因为时间无限制那么可以流多次)。所以一张网络图能解决的问题仅仅是他们能否到达对岸。(其实如果仅仅是这个问题的话我们不用最大流也能搞定)


所以此题的关键在于时间因素。因为我们需要限定最小时间,那么我们就需要去枚举在多少时间后我们能够完成所有人的运送。也就意味着不同的时间对应了不同的网络图,因为时间不同边的流量转移也会发生变化。那么如何理解这个问题呢?我们将所有石头拆点成为每一时刻的该石头(比如有n个石头,时间为t,那么就粗略地有n*t个节点。)表示该石头在时间t时所处的状态。而且因为是点流量限制,需要进行拆点,负载为承受量。(下文的论述默认为拆点过后的状态,拆点的出点和入点区分请自行完成思考。)


于是,对于tt时刻,我们将超级源点 src 与src能够到达的石头在tt时刻的状态节点相连,负载为INF。意义是从源点可以源源不断的跳到第一跳石头。然后将所有能够到达超级汇点des的石头在tt-1时刻的状态节点与des相连,负载为INF。意义是从这些石头总是一秒就可以跳到对岸。(注意理解为什么是tt-1而不是tt,因为我们等会儿要跑最大流,那么跳到对岸还要花1s,所以tt时刻能到达des的肯定是上一秒就准备好了的那些节点)。


然后重点就来了:对于某石头在tt时刻的状态节点 Si_tt,对于它所有能够到达的周围节点Sj,我们从Sj_tt-1 向Si_tt连一条边,负载为INF,表示上一个时刻tt-1到达石头j的人,可以在这一时刻跳到Si_tt。此时得到的图是tt时刻的状态图。构图好了之后跑最大流,表示第tt秒能够过去多少人(用ans统计tt秒内过河的总人数),此时得到一个残余网络。如果ans已经>=m了,说明OK了,就退出。否则继续重复进行tt+1时刻的加边构图工作。


对于第一组数据,我们可以给出这样的随着时间的整个网络流量表:

t = 1s, flow = 0; 

t = 2s, flow = 0;

t = 3s, flow = 3; 

t = 4s, flow = 3;

t = 5s, flow = 3; 

t = 6s, flow = 3;

所以到6秒就可以过去12个人了>=10个人

注意如果能过河时间不会超过n+m,因为大不了一个人一个人的过河也只需要n+m。所以如果tt超过了n+m说明无解。另外还有一个疑问是如果按照时间将一个石头分为多个重复的节点,会不会一个石头上的人数其实超量了。其实不会,因为如果能够过河的话是不存在超量问题的(想想为什么?),除非不能过河,那么最后的结果也是不能过河,而不会是错误答案。我的代码中+200是去了一个很宽的跳跃防止出错,其实你可以自己调整。


上代码:

#include
#include
#include
#include
#include
#include
using namespace std;

const int MAXN = 42000;
const int MAXM = 9600000;
const int INF = 0x3f3f3f3f;

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

struct Point
{
	double x, y;
	int cap;
};

Edge edge[MAXM];
Edge con[MAXN];
Point p[MAXN];
int head[MAXN];
int headcon[MAXN];
int level[MAXN];
int src, des, cnt, cnt1, ans;

double dist( Point a, Point b )
{
	return sqrt( (a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y) );
}

void addcon( int from, int to )
{
	con[cnt1].from = from;
	con[cnt1].to = to;
	con[cnt1].next = headcon[from];
	headcon[from] = cnt1++;

	swap( from, to );

	con[cnt1].from = from;
	con[cnt1].to = to;
	con[cnt1].next = headcon[from];
	headcon[from] = cnt1++;
}


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++;

	swap( from, to );

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

int bfs( )
{
	memset( level, -1, sizeof level );
	queue 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] + 1)
		{
			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 )) > 0)
		{
			ans += tem;
		}
	}
	return ans;
}

int main( )
{
	int n, m, d, w;
	src = 0;
	des = 5500;

	while (cin >> n >> m >> d >> w)
	{
		memset( head, -1, sizeof head );
		memset( headcon, -1, sizeof headcon );
		ans = cnt = cnt1 = 0;

		for (int i = 1; i <= n; i++)
		{
			cin >> p[i].x >> p[i].y >> p[i].cap;
		}

		if (d >= w)
		{
			cout << 1 << endl;
			continue;
		}

		for (int i = 1; i <= n; i++)
		{
			if (p[i].y <= d)
				addcon( src, i );
			if (w - p[i].y <= d)
				addcon( i, des );

			for (int j = i + 1; j <= n; j++)
			{
				if (dist( p[i], p[j] ) <= d)
					addcon( i, j );
			}
			addcon( i, i );
		}


		int t;
		for (t = 1; t <= n + m; t++) // time 
		{

			for (int j = headcon[src]; j != -1; j = con[j].next)
			{
				int v = con[j].to;
				addedge( src, t * 200 + v, INF );
			}


			for (int k = 1; k <= n; k++) //rocks
			{
				for (int j = headcon[k]; j != -1; j = con[j].next)
				{
					int v = con[j].to;
					if (v == des)
						addedge( (t - 1) * 200 + k + 50, des, INF );
					else
						addedge( (t - 1) * 200 + k + 50, t * 200 + v, INF );
				}
				addedge( t * 200 + k, t * 200 + k + 50, p[k].cap );
			}

			int temp = Dinic( );
			ans += temp;
			if (ans >= m) break;
		}
		if (t > n + m)
			cout << "IMPOSSIBLE" << endl;
		else
			cout << t << endl;
	}
	return 0;
}

今天上个视频处理也在讲最大流,简直是醉了。应该叫醉大流。

你可能感兴趣的:(ACM_最大流,ACM)