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 }