The Glorious Karlutka River =)

sgu438:http://acm.sgu.ru/problem.php?contest=0&problem=438

题意:有一条东西向流淌的河,宽为 W,河中有 N 块石头,每块石头的坐标(Xi, Yi)和最大承受人数 Ci 已知。现在有 M 个游客在河的南岸,他们想穿越这条河流,但是每个人每次最远只能跳 D 米,每跳一次耗时 1 秒。问他们能否全部穿越这条河流,如果能,最少需要多长时间。 <= N <= 50, 0 < M <= 50, 0 <= D <= 1000, 0 < W(0<= 1000, 0 < Xi < 1000, 0 < Yi < W, 0 <= Ci <= 1000)。刚看完这题,想当然的认为它是一道最小费用流问题。但是当WA之后我才明白,这题并不是去求一个给定网络的最大流,而是计算这个网络随着时间推移每次能够留出多少流量。我们通过枚举时间的方式来决定在什么时刻能够把所有的人全部送到对岸。注意人是可以从河这岸的任意x坐标出发的。

题解:这是一道费用流。具体的看代码吧。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<queue>
  6 #include<cmath>
  7 #define inf 100000000
  8 using namespace std;
  9 const int E=10000;
 10 const int N=302;
 11 struct Node{
 12    int v, cap, cost, next;    //  re记录逆边的下标。
 13 }edge[E];
 14 int n, m;
 15 int  ans;
 16 int k, head[N];
 17 int que[N], pre[N], dis[N];
 18 bool vis[N];
 19 void init(){//初始化
 20    k=ans=0;
 21    memset(head,-1,sizeof(head));
 22 }
 23 void addEdge(int u, int v, int ca, int co){
 24     edge[k].v = v;
 25     edge[k].cap = ca;
 26     edge[k].cost = co;
 27     edge[k].next = head[u];
 28     head[u] = k ++;
 29     edge[k].v = u;
 30     edge[k].cap = 0;
 31     edge[k].cost = -co;
 32     edge[k].next = head[v];
 33     head[v] = k ++;
 34 }
 35 bool spfa(){                  //  源点为0,汇点为n。
 36     int i;
 37     for(i = 0; i <=2*m+1;i++){
 38         dis[i] = inf;
 39         vis[i] = false;
 40     }
 41     queue<int>Q;
 42     Q.push(0);
 43     dis[0]=0;
 44     vis[0] = true;
 45     while(!Q.empty()){       //  这里最好用队列,有广搜的意思,堆栈像深搜。
 46         int u = Q.front();
 47         Q.pop();
 48         vis[u]=0;
 49         for(i = head[u]; i != -1; i = edge[i].next){
 50             int v = edge[i].v;
 51             if(edge[i].cap && dis[v] > dis[u] + edge[i].cost){
 52                 dis[v] = dis[u] + edge[i].cost;
 53                 pre[v] = i;
 54                 if(!vis[v]){
 55                     vis[v] = true;
 56                    Q.push(v);
 57                 }
 58             }
 59         }
 60         vis[u] = false;
 61     }
 62     if(dis[2*m+1] == inf) return false;
 63     return true;
 64 }
 65 int  end(){
 66     int u, p, sum = inf;
 67     for(u = 2*m+1; u != 0; u = edge[p^1].v){//0是超级源点
 68         p = pre[u];
 69         sum = min(sum, edge[p].cap);
 70     }
 71     for(u = 2*m+1; u != 0; u = edge[p^1].v){
 72         p = pre[u];
 73         edge[p].cap -= sum;
 74         edge[p^1].cap += sum;
 75     }
 76     return sum;
 77 }
 78 double d,w;
 79 struct  Point{
 80    double x;
 81    double y;
 82    int val;
 83 }num[60];
 84 int main(){
 85    while(~scanf("%d%d%lf%lf",&m,&n,&d,&w)){
 86         init();//初始化
 87        for(int i=1;i<=m;i++){
 88         scanf("%lf%lf%d",&num[i].x,&num[i].y,&num[i].val);
 89        }
 90        for(int i=1;i<=m;i++){
 91           if(abs(num[i].y)<=d){
 92               addEdge(0,i,n,1);
 93           }
 94        }
 95        for(int i=1;i<=m;i++){
 96           for(int j=1;j<=m;j++){
 97              if(i==j)continue;
 98              double diss=sqrt((num[i].x-num[j].x)*(num[i].x-num[j].x)+(num[i].y-num[j].y)*(num[i].y-num[j].y));
 99              if(diss<=d){
100                 addEdge(i+m,j,inf,1);
101              }
102           }
103           if(abs(num[i].y-w)<=d){
104             addEdge(i+m,2*m+1,inf,1);
105           }
106           addEdge(i,i+m,num[i].val,0);
107        }
108        if(w<=d)addEdge(0,2*m+1,inf,1);
109        int  s=n,now=0,sum=0;
110        ans=inf;
111        while(spfa()){
112           int y=end();
113            s-=(dis[2*m+1]-now)*sum+y;
114            if(s<0)s=0;
115            sum+=y;now=dis[2*m+1];
116            int temp=now+(int)ceil(s*1.0/sum);
117            if(temp<ans)ans=temp;
118        }
119        if(ans==inf)printf("IMPOSSIBLE
");
120        else
121        printf("%d
",ans);
122    }
123 }
View Code
原文地址:https://www.cnblogs.com/chujian123/p/3948582.html