题意:在空间中给定n个点,给出每个点的三维坐标,以及每个点上已有的花的数目Fi,和可以从该点上移走的花的数目Li,因为每一次可以移动的距离有限,为R, 所以可以选择一个中介点进行转移,最后将所有的花都移动到第一个点上。
因为可以从每个点上移走的花的数目有限,所以,可以通过修改每次可以移动的距离R, 使得最终可以将所有的花移动到第一个点。 求最小的R。 若不存在,这输出-1
分析:求最小的R, 我们可以二分,转化为判定性问题。 如果已知R, 问是否能将所有的花移动到第一个点呢? 问题是否好解决一点了?? 将所有的花移动到第一个点,其实就是一个很明显的网络流判是否满流的问题了。
问题就是构图了。
虚拟一个源点,汇点就比较明显了,就是第一个点。
首先是拆点,将每一个点拆成一个 i点和 一个i +n, 连边,容量为Li,, 这是容量限制,表示经过该i点最多能有Li朵花(根据题目要求)
接着,根据R的距离,将可以转移的点连边, (注意是无向边,而且点已经拆成入点跟出点了,),容量为INF, 无穷大
再来是汇点,将所以与第一个点满足距离要求的点连边,容量也为INF
从源点往所有点连一条边,容量为Fi, 最后判断是否满流
zoj3691
#include<iostream> #include<algorithm> #include<stdio.h> #include<stdlib.h> #include<string.h> #include<math.h> using namespace std; const int N = 210; const int M = 80010; const double eps = 1e-7; const int INF = 100000000; int size,head[N],dis[N],gap[N],pre[N],cur[N]; struct edge { int v,w,next; edge(){} edge(int v,int next,int w=0):v(v),next(next),w(w){} }E[M]; inline void insert(int u,int v,int w) { E[size]=edge(v,head[u],w); head[u]=size++; E[size]=edge(u,head[v],0); head[v]=size++; } int ISAP(int src,int des, int n) { int maxflow=0; memset(dis,0,sizeof(dis)); memset(gap,0,sizeof(gap)); for(int i=0;i<=n;i++) cur[i]=head[i]; int u=pre[src]=src; int aug=-1; while(dis[src]<n) { loop: for(int &i=cur[u];i!=-1;i=E[i].next) { int v=E[i].v; if(E[i].w && dis[u]==dis[v]+1) { aug=min(aug,E[i].w); pre[v]=u; u=v; if(v==des){ maxflow+=aug; for(u=pre[u];v!=src;v=u,u=pre[u]) { E[cur[u]].w-=aug; E[cur[u]^1].w+=aug; } aug=INF; } goto loop; } } int mdis=n; for(int i=head[u];i!=-1;i=E[i].next) { int v=E[i].v; if(E[i].w && mdis>dis[v]) { cur[u]=i; mdis=dis[v]; } } if((--gap[dis[u]])==0) break; gap[dis[u]=mdis+1]++; u=pre[u]; } return maxflow; } void init() { memset(head,-1,sizeof(head)); size=0; } int F[N], L[N], x[N], y[N], z[N]; double dist[N][N]; inline double getDist(int x1, int y1, int z1, int x2, int y2, int z2) { return sqrt(double((x1 - x2) * 1ll * (x1 - x2) + (y1 - y2) * 1ll * (y1 - y2) + (z1 - z2) * 1ll * (z1 - z2))); } void init_Dist(int n) { for(int i = 1; i <= n; ++i) for(int j = i + 1; j <= n; ++j) { dist[i][j] = getDist(x[i], y[i], z[i], x[j], y[j], z[j]); dist[j][i] = dist[i][j]; } } void build(double mid, int n) { for(int i = 2; i <= n; ++i)//拆点, 限容 insert(i, i + n, L[i]); for(int i = 2; i <= n; ++i)//连边 for(int j = i + 1; j <= n; ++j) if(dist[i][j] <= mid) { insert(i + n, j, INF); insert(j + n, i, INF); } for(int i = 2; i <= n; ++i)//汇点 { if(dist[1][i] <= mid) insert(i + n, 1, INF); } for(int i = 2; i <= n; ++i)//源点 insert(2 * n + 1, i, F[i]); } int main() { int n; while(scanf("%d",&n) == 1) { bool flag = true; int tot = 0; for(int i = 1; i <= n; ++i) { scanf("%d %d %d %d %d", &x[i], &y[i], &z[i], &F[i], &L[i]); tot += F[i]; } tot -= F[1]; init_Dist(n); double left = 0, right = 50000.0; double ans = -1; while(right - left > eps)//二分枚举答案 { double mid = (left + right) / 2.0; init(); build(mid, n); int tmp = ISAP(2 * n + 1, 1, 2 * n + 1); if(tmp == tot) { ans = mid; right = mid; } else left = mid; } if(ans < 0) puts("-1"); else printf("%.7f\n",ans); } return 0; }