sgu438 动态流

438. The Glorious Karlutka River =)
Time limit per test: 0.5 second(s)
Memory limit: 262144 kilobytes
input: standard
output: standard



A group of  M   tourist s 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  tourist s 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.  tourist s know that the river is  W  meters wide, and they have estimated the coordinates of rubbish piles ( X i Y i ) and the capacity of each pile ( C i , the maximum number of  tourist s 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.  tourist s start on the river bank at 0 by  Y  axis. The  Y  coordinate of the opposite bank is  W .

tourist s 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  tourist 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  tourist s will be able to cross the river, or the line " IMPOSSIBLE " if it is impossible to cross the river.

Example(s)
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


题意:有一条河,河上有一些垃圾(看成点),人借助垃圾跳到对岸,已知垃圾坐标位置,人每次最大跳跃距离,河的宽度以及人数,问是否所有人可以跳到对岸,若可以,求出最短时间。

这题需要求时间,显然是个动态建模问题,与家园一题的大致思想类似。传送门:家园 

但是难度比家园稍大,很显然要暴力枚举时间,知道所有人都跳过为止。我们知道时间的上限是n+m,因为对于第一个人他从一河岸跳到对岸最多踩m个垃圾,加上到达对岸的一步,共需花m+1单位的时间,而第二个人他可以在第一人跳到第二个垃圾时重复第一人的跳法,也就是后面一人比前面一人晚到对岸1个单位时间,那么有n-1人重复此过程,因此最大时间m+1+n-1=m+n,若超过此时间,所有人未到对岸,显然是无解情况。

对于有解情况:我们先预处理所有垃圾两两之间的距离是否小于等于D,也就是能否在这些垃圾之间跳来跳去,

麻烦的是河岸并不是一个点,而是一条线,因此我们也要预处理能从左岸(出发地)跳到的垃圾(y坐标<=D),和能从哪些垃圾跳到对岸(y坐标+D>=M).

然后根据时间拆点,记i点在t时刻的状态为(i,t),这里注意t=1,就是直接从左岸跳到对岸,这个可以判断D>=M求得,然后我们从t=2开始枚举,这里要考虑清楚,我之前建模没想明白,t-1时刻,人是站在(i,t-1)状态上的,那么t时刻人可以从(i,t-1)跳到对岸(如果满足条件的话),也就是要将所有(i,t-1)连向汇点,

千万不要将(i,t)连向汇点,当然也可以选择从(i,t-1)跳到(j,t)(i,j距离<=D),当然不要忘了从左岸连向那些y坐标<=D的点(一步可达点),这样就差不多了。

最后别忘了,每个点需要拆成两个点,不这样做的话会有大量流量聚集在i点,然后通过下一时刻流出去,这样就超过i在某一时刻最大人数限制了。此题拆点是挺麻烦的。

PS:这里最多会有10000割点左右,最坏情况有10^8条边,显然内存不允许,dinic算法也会TLE,所以可以推断,数据显然不会出现这么多边和点的情况。所以随便开了2000000就AC。

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#define eps 1e-6
#define Maxn 15000
using namespace std;

const int step=6500;
const int mstep=60;
double x[60],y[60];
int num[60],dist[60][60],ck1[60],ck2[60];
bool dis(int i,int j,double D){
    double dt=(x[i]-x[j])*(x[i]-x[j])+
    (y[i]-y[j])*(y[i]-y[j]);
    return dt<D*D||fabs(dt-D*D)<eps;
}
const int inf=0x3f3f3f3f;
struct line{
    int to,next,cap;
}p[2000000];
int head[Maxn];
int q[Maxn];
int d[Maxn];
int tot;
int src,t;
int n,m;
void addedge(int a,int b,int c){
    p[tot].to=b;
    p[tot].next=head[a];
    p[tot].cap=c;
    head[a]=tot++;
}
void insert(int a,int b,int c){
    addedge(a,b,c);
    addedge(b,a,0);
}
bool bfs(){
    memset(d,-1,sizeof d);
    int s=0,e=-1;
    q[++e]=src;
    d[src]=0;
    while(s<=e){
        int u=q[s++];
        for(int i=head[u];i!=-1;i=p[i].next){
            int v=p[i].to;
            if(d[v]==-1&&p[i].cap){
                d[v]=d[u]+1;
                q[++e]=v;
            }
        }
    }
    return d[t]!=-1;
}
int dfs(int u,int alpha){
    if(u==t) return alpha;
    int w,used=0;
    for(int i=head[u];i!=-1&&used<alpha;i=p[i].next){
        int v=p[i].to;
        if(p[i].cap&&d[v]==d[u]+1){
            w=dfs(v,min(alpha-used,p[i].cap));
            used+=w;
            p[i].cap-=w;
            p[i^1].cap+=w;
        }
    }
    if(!used) d[u]=-1;
    return used;
}
int dinic(int i){
    int ans=0;
    src=0,t=n+1+mstep*i;
    while(bfs())
        ans+=dfs(src,inf);
    return ans;
}
int main()
{
    int D,W;
    while(cin>>n>>m>>D>>W){
        for(int i=1;i<=n;i++)
            cin>>x[i]>>y[i]>>num[i];
        if(D>=W||fabs(D-W)<eps) {puts("1");continue;}
        tot=0;
        memset(head,-1,sizeof head);
        memset(ck1,0,sizeof ck1);
        memset(ck2,0,sizeof ck2);
        for(int i=1;i<=n;i++){
            if(y[i]<D||fabs(D-y[i])<eps) ck1[i]=1;
            if(y[i]+D>W||fabs(y[i]+D-W)<eps) ck2[i]=1;
        }
        memset(dist,0,sizeof dist);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(dis(i,j,D)) dist[i][j]=1;
        for(int i=1;i<=n;i++) //初始化时刻1
            if(ck1[i]){
                insert(0,i+mstep,inf);
                insert(i+mstep,i+mstep+step,num[i]);
            }
        int res=m,ans;
        bool flag=false;
        for(int i=2;i<=n+m;i++){
            for(int j=1;j<=n;j++){
                insert(j+i*mstep,j+i*mstep+step,num[j]); //拆点
                if(ck1[j]) insert(0,j+i*mstep,inf); //源点连向此时刻
                if(ck2[j]) insert(j+(i-1)*mstep+step,n+1+mstep*i,inf); //前一时刻连向汇点
            }
            for(int j=1;j<=n;j++)
                for(int k=1;k<=n;k++)
                    if(dist[j][k]) insert(j+(i-1)*mstep+step,k+i*mstep,inf);
            res-=dinic(i);
            if(res<=0){
                flag=true;
                ans=i;
                break;
            }
        }
        if(flag) printf("%d\n",ans);
        else puts("IMPOSSIBLE");
    }
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值