Description
Input
Output
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;
注意如果能过河时间不会超过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;
}