ZOJ 3691 Flower

/*


题目: Flower
题目连接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4974
题目大意:

   三维空间中有n个点,序号为1-n,每个点有一个坐标,每个点上有F[i]朵花,每个点只允许L[i]朵花从这个点转移出到
别的点,每两个点之间的距离为欧几里得距离,现在Gao的女朋友要把序号2-n点上的花全部转移到1号点上,一个点上的花要
转移到1号点上可以经过别的点中转,但要符合L[i]的限制,但是Gao的女朋友一次能走的距离是R,如果Gao的女朋友把i号点花转
移到j号点上,Gao的女朋友需要行走的距离是dis(i,j),现在问:Gao的女朋友要完成这项任务需要的最小R,如果完成不了输出-1。


解题思路:

遇到此类型的题,第一个感觉就是二分R,因为这里的R是单调的(因为小R符合,大R肯定符合,所以是单调的)现在问题在于怎么
判定R的合法性,通过对题目的分析,每个点有一个L[i],F[i]值,且L[i]值决定了最大流出量,而我们最后是要把所有的花都转移到1号点
那么2-n号点的总F[i]值决定了总流量,现在的判定就变成了在已知所有点的最大流出量能不能使最后流到1号点的总流量等于sum{F[i]}(1<i<=n)
的问题,这就是一个最大流模型了,当遇到点有流量时,我们最常见的做法就是拆点,把i点拆成i,i+n点,并在i点和i+n之间建一条边,边的流量
值等于i点流量值,流进i的流量还是流进i点,而从i点流出的流量拆点后变成了从i+n点流出,因为在这个图中还缺一个源点,我们增加一个源点0,

0点流向2-n号点,且流向每个点的流量为F[i],最后原问题就变成了判定从0点到1点的最大流和sum{F[i]}(1<i<=n)是否相等,如果相等表示此R是可以的,

否则此R是不合法的,就这样一直二分R下去,如果二分到最后R的值大于所有两点之间的距离,那么此题无解。


 如果对网络流不熟悉的同学,请先看看网络流的相关资料,比如dinic算法,gap优化等方面(刘汝佳老师的白书上就有)。

*/


#include<math.h>
#include<stdio.h>
#include<string.h>
#include<queue>

using namespace std;

#define inf 2000000000

struct node
{
	double x,y,z;
}point[110];

double dis(node a,node b)
{
	return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z));
}

int F[110],L[110];

double mp[110][110],Max;
void init(int n)
{
	int i,j;Max=0;
	for(i=1;i<=n;i++)
		for(j=1;j<=n;j++)
		{
			mp[i][j]=dis(point[i],point[j]);
			if(mp[i][j]>Max)
				Max=mp[i][j];
		}
}

#define N 300
#define M 100000

struct edge
{
    int from,to,c,next;
}edge[M];
int ant,head[N],dep[N],gap[N];
void add(int a,int b,int c)
{
    edge[ant].from=a;
    edge[ant].to=b;
    edge[ant].c=c;
    edge[ant].next=head[a];
    head[a]=ant++;
    edge[ant].from=b;
    edge[ant].to=a;
    edge[ant].c=0;
    edge[ant].next=head[b];
    head[b]=ant++;
}

void BFS(int start,int end)
{    
    int i,to,u;
    memset(dep,-1,sizeof(dep));
    memset(gap,0,sizeof(gap));
    queue < int > que ;
    dep[end]=0;
    gap[0]=1;
    que.push(end);
    while(!que.empty())
    {
        u=que.front();
        que.pop();
        for(i=head[u];i!=-1;i=edge[i].next)
        {
            to=edge[i].to;
            if(dep[to]!=-1||edge[i].c!=0)continue;
            que.push(to);
            dep[to]=dep[u]+1;
            ++gap[dep[to]];
        }
    }
}

int s[N],cur[N];
int Gap(int start,int end,int n)
{
    int res=0;
    BFS(start,end);
    int u=start,top=0;
    memcpy(cur,head,sizeof(head)); 
    while(dep[start]<n)
    {
        int i;
        if(u==end)
        {
            double Min=inf;
			int flag;
            for(i=0;i<top;i++)
            {
                if(edge[s[i]].c<Min)
                {
                    Min=edge[s[i]].c;
                    flag=i;
                }
            }
            res+=Min;
            for(i=0;i<top;i++)
            {
                edge[s[i]].c-=Min;
                edge[s[i]^1].c+=Min;
            }
            top=flag;
            u=edge[s[top]].from;
        }
        if(u!=end&&gap[dep[u]-1]==0)break;
        for(i=cur[u];i!=-1;i=edge[i].next)
        {
            if(edge[i].c!=0&&dep[edge[i].to]+1==dep[u])
                break;
        }
        if(i!=-1)
        {
            cur[u]=i;
            s[top++]=i;
            u=edge[i].to;
        }
        else 
        {
            int Min=n;
            for(i=head[u];i!=-1;i=edge[i].next)
            {
                if(edge[i].c==0)continue;
                if(Min>dep[edge[i].to])
                {
                    cur[u]=i;
                    Min=dep[edge[i].to];
                }
            }
            --gap[dep[u]];
            dep[u]=Min+1;
            ++gap[dep[u]];
            if(u!=start)
                u=edge[s[--top]].from;
        }    
    }
    return res;
}

int judge(double width,int n)
{
	int i,j;
	int maxf=0; ant=0;
	memset(head,-1,sizeof(head));
	for(i=1;i<=n;i++)
	{
		for(j=i+1;j<=n;j++)
		{
			if(mp[i][j]<=width)
			{
				add(i+n,j,inf);
				add(j+n,i,inf);
			}
		}
		add(i,i+n,L[i]);
		if(i>1){
			add(0,i,F[i]);
			maxf+=F[i];
		}
	}
	return Gap(0,1,2*n+1)==maxf;
}

int main()
{
	int n,i;
	double l,r,mid;
	while(scanf("%d",&n)!=EOF)
	{
		for(i=1;i<=n;i++)
		{
			scanf("%lf%lf%lf%d%d",&point[i].x,&point[i].y,&point[i].z,&F[i],&L[i]);
		}
		init(n);
		r=1e20,l=0;
		while(r-l>0.00000001)
		{	
			mid=(l+r)/2;
			if(judge(mid,n))
				r=mid;
			else 
				l=mid;
		}
		if(mid>Max) printf("-1\n");
		else printf("%.7lf\n",mid);
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值