题意 : 在X,Y坐标系上有n个冰块,冰块上有一些企鹅,每个企鹅的跳跃距离都是d ,每个企鹅从一个冰块跳到另一个冰块时,冰块消耗一点承受度,冰块的承受压力是mi ,;
也就是每个冰块上的企鹅最多不能离开mi只 , 这些企鹅最终要汇聚在一块冰块上 ,求有哪些冰块可以当作聚集地 。
把n个冰块拆点为i,i+n , i只能企鹅进入,企鹅只能从i+n离开 ; i~i+n 流量为mi ,这样可以控制经过冰块的企鹅不会超过它的承受度 ; 源点s到每个冰块脸边,流量为冰块上企鹅的数量 ,然后冰块与冰块之间 ,如果i冰块与j冰块之间的距离小于d ,也就是这两个冰块上的企鹅可以互相跳过来 ; 那么连边 i冰块出口到j冰块入口,add(i+n,j,inf) ;
j冰块也可以去i冰块上面 add(j+n,i,inf) ; 然后枚举聚集地,若假设i冰块时最后的聚集地, 那么它的拆点之间流量改为inf ,因为所有企鹅都到它这里来,不会从该冰块出口离开 ;
不能限制流量为mi ; 最后聚集地到汇点连边 ,流量inf ; 跑一边最大流 ,最大流等于企鹅数量,即答案合理 !
注意存边开大一点 ,起码要10万; 还有两点间距离容易出错 ,该double的要double ! 坑了我一晚上 .
。
#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<stack>
#include<queue>
#include <iomanip>
#include<iostream>
#include<algorithm>
using namespace std ;
const int N=500 ;
const int M=100000 ;
const int inf=1<<30 ;
struct node
{
int u ,v,c,next;
}edge[M] ;
struct node1
{
int n,m ;
double x,y;
}e[M] ;
int head[N],dis[N],gap[N],cur[N],pre[N] ;
int top , s,t,sum,n,nv;
double d ;
double Dis(int i,int j)
{
return sqrt(1.0*(e[i].x-e[j].x)*(e[i].x-e[j].x)+(e[i].y-e[j].y)*(e[i].y-e[j].y));
}
void add(int u ,int v,int c)
{
edge[top].u=u;
edge[top].v=v;
edge[top].c=c;
edge[top].next=head[u];
head[u]=top++;
edge[top].u=v;
edge[top].v=u;
edge[top].c=0;
edge[top].next=head[v];
head[v]=top++;
}
int sap()
{
memset(dis,0,sizeof(dis));
memset(gap,0,sizeof(gap));
int u,v,minflow=inf,flow=0;
for(int i = 0 ; i <=nv ; i++) cur[i]=head[i] ;
u=pre[s]=s;
gap[s]=nv;
while(dis[s] <= nv )
{
loop :
for(int &j=cur[u] ; j!=-1 ; j=edge[j].next)
{
v=edge[j].v ;
if(edge[j].c > 0 && dis[u] == dis[v] +1 )
{
minflow = min(minflow ,edge[j].c) ;
pre[v]=u;
u=v ;
if(v==t)
{
for( u =pre[v] ; v!=s ;v=u,u=pre[u])
{
edge[cur[u]].c -= minflow ;
edge[cur[u]^1].c += minflow ;
}
flow += minflow ;
minflow = inf ;
}
goto loop ;
}
}
int mindis=nv ;
for(int i = head[u] ; i!=-1 ; i=edge[i].next)
{
v=edge[i].v ;
if(edge[i].c > 0 && dis[v] < mindis)
{
mindis = dis[v] ;
cur[u]=i ;
}
}
if(--gap[dis[u]]==0) break ;
gap[ dis[u] = mindis +1 ]++ ;
u=pre[u] ;
}
return flow ;
}
void make(int mid)
{
memset(head,-1,sizeof(head)) ;
top = 0 ;
s=0,t=2*n+1 ,nv=t+1 ;
for(int i = 1 ; i <= n ; i++)
{
add(s,i,e[i].n) ; //源点连冰块
if(i==mid) //聚集地之间流量没有来限制
add(i,i+n,inf) ;
else add(i,i+n,e[i].m) ;//限制企鹅数量
}
for(int i = 1 ; i <= n ;i++ )
for(int j = i+1; j <= n ; j++)
{
if( Dis(i,j) <= d ) //i,j冰块可以互相到达
{
add(i+n,j,inf) ,add(j+n,i,inf) ;//互相可以跳上去
}
}
}
int main()
{
int tt ,f[200];
scanf("%d",&tt) ;
while(tt--)
{
scanf("%d%lf",&n,&d) ;
sum = 0;
for(int i = 1 ; i <= n ; i++)
{
scanf("%lf%lf%d%d",&e[i].x,&e[i].y,&e[i].n,&e[i].m) ;
sum += e[i].n ;
}
int k = 0 ;
for(int i = 1 ; i <= n ; i++)
{
make(i) ;
add(i+n,t,inf) ; //聚集地到汇点连边
if(sap()==sum) //i冰块可以作为聚集地
{
f[k++]=i-1 ;
}
}
if(k==0)
printf("-1\n") ;
else
{
printf("%d",f[0]) ;
for(int i = 1 ; i < k ; i++)
printf(" %d",f[i]) ;
puts("") ;
}
}
return 0 ;
}
本文探讨了一种基于最大流算法解决企鹅跳跃问题的策略,涉及企鹅跳跃距离、冰块承受度及最终聚集地的选择。通过构建网络流模型,实现了对企鹅流动路径的有效控制,确保冰块不超载,同时计算出合理的聚集地。此方法不仅巧妙地融合了图论中的最大流概念,还提供了实用的解决方案。

977

被折叠的 条评论
为什么被折叠?



