这道题终于又了结果,哈哈,真开心!
其实这题不难,就是有点麻烦
题意:有n个点,每个点上有ni只企鹅,每个点只能允许mi只企鹅跳离这个点;并且每只企鹅,一次最多能跳D这么长的距离;目的是让左右企鹅能在一个点聚会,那么问,哪些点能作为聚会的点,一次输出这些点,如果没有方案那么就是输出-1。(n<=100, 1<= mi <= 200, 0<=ni<= 10, D <=100000)输出包括点数,D,每个点坐标以及每个点的ni和mi值。
分析:
让所有企鹅在同一点聚会,那么显然是看流入是否等于流出,最大流问题;
每个点有流量mi,显然,拆点,容量mi,这里设原来的i点为2i,并且和s直接相连,拆出来的为2i+1,如果要跳离这个点,那么就要经过这个拆点得来的边,而要是跳上这个点,那就直接连到2i这个点,因为没有花费;
每个点,最开始很可能就有企鹅,那么就相当于流入量,需要超级源点s,使得s进入i点的流量为ni;
要注意的是,一个点可以有无数只企鹅跳上来,只要不从这个点离开,那么就不花费流量,也就是说,汇点,应该从和从s直接相连的点里面,一次枚举;
还有,对于原来的点来说,点和点之间是双向的,双向流量都为INF
代码如下:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <algorithm>
using namespace std;
#define For(i, s, e) for( int i = s; i < e; ++i )
#define esp 1e-9
const int N = 220;
const int INF = 1000000;
int p[N], a[N], T, n,cap[N][N], flow[N][N], mi[N], ni[N];
double D, x[N], y[N];
int maxFlow( int s, int t ) {
queue<int>q;
memset(flow, 0, sizeof(flow));
int f = 0;
while ( 1 ) {
memset(a, 0, sizeof(a));
a[s] = INF;
q.push(s);
while ( !q.empty() ) {
int u = q.front(); q.pop();
For(v, 0, 2*n+1) {
if( !a[v] && cap[u][v] > flow[u][v] ) {
a[v] = min( a[u], cap[u][v] - flow[u][v]);
p[v] = u;
q.push(v);
}
}
}
if ( a[t] == 0 ) break;
for ( int u = t; u != s; u = p[u] ) {
flow[p[u]][u] += a[t];
flow[u][p[u]] -= a[t];
}
f += a[t];
}
return f;
}
int main() {
scanf("%d", &T);
while ( T-- ) {
scanf("%d%lf", &n, &D);
int sum = 0, f = 0;
For(i, 0, n) {
scanf("%lf%lf%d%d", &x[i], &y[i], &ni[i], &mi[i]);
sum += ni[i];
}
memset( cap, 0, sizeof(cap));
int s = 2*n, e = s+1;
//将源点与每个点相连,然后容量为这个点上的企鹅的个数;将每个点拆为2i和2i+1,容量为mi[i]
For(i, 0, n) {
cap[s][2*i] = ni[i];
cap[2*i][2*i+1] = mi[i];
}
//建图,将距离小于D的点连起来,容量为INF
For(i, 0, n)
For(j, 0, n) {
double tmp = sqrt( (x[i]-x[j])*(x[i]-x[j]) + (y[i]-y[j])*(y[i]-y[j]));
if ( tmp - D <= esp ) cap[2*i+1][2*j] = INF;
}
For(i, 0, n) {
int tmp = maxFlow( s, 2*i );
//printf("tmp %d\n", tmp);
if ( tmp >= sum ) {
f++;
if ( f > 1 ) printf(" ");
printf("%d", i);
}
}
if ( f == 0 ) printf("-1");
printf("\n");
}
}