【bzoj5037】[Jsoi2014]电信网络 最大权闭合图
题目描述
JYY创建的电信公司,垄断着整个JSOI王国的电信网络。JYY在JSOI王国里建造了很多的通信基站。目前所有的基站都是使用2G网络系统的。而现在3G时代已经到来了,JYY在思考,要不要把一些基站升级成3G网络的呢?JSOI王国可以被看作为一个无穷大的二维平面,JYY一共建造了N个通信基站,第i个基站的坐标是(Xi,Yi)。每个基站有一个通信范围Ri。第i号基站会向所有到其距离不超过Ri的基站发送信息。每个基站升级到3G网络都会有一个收益Si,这个收益可能是正数(比如基站附近有个大城市,用户很多,赚的流量费也就很多了),也可能是负数(比如基站周围市场不佳,收益不能填补升级基站本身的投资)。此外,由于原有的使用2G网络系统的基站无法解析从升级成3G网络系统的基站所发来的信息(但是升级之后的基站是可以解析未升级基站发来的信息的),所以,JYY必须使得,在升级工作全部完成之后,所有使用3G网络的基站,其通信范围内的基站,也都是使用3G网络的。由于基站数量很多,你可以帮助JYY计算一下,他通过升级基站,最多能获得的收益是多少吗?
输入
第一行一个整数N;
接下来N行,每行4个整数,Xi,Yi,Ri,Si,表示处在(Xi,Yi)的基站的通信范围是Ri,升级可以获得的收益是Si。数据满足任意两个基站的坐标不同。
1≤N≤500,1≤Ri≤20000,|Xi|,|Yi|,|Si|≤10^4。
输出
输出一行一个整数,表示可以获得的最大收益。
样例输入
5
0 1 7 10
0 -1 7 10
5 0 1 -15
10 0 6 10
15 1 2 -20
样例输出
5
题解
最大权闭合图
选xxx就必须选xxx,求最大总收益,显然最大权闭合图问题。
用两点间距离公式求出选某个就必须选哪些,然后建图最小割,没了。。
#include <queue> #include <cstdio> #include <cstring> #define N 510 #define M 1000010 #define inf 1 << 30 using namespace std; queue<int> q; int x[N] , y[N] , r[N] , v[N] , head[N] , to[M] , val[M] , next[M] , cnt = 1 , s , t , dis[N]; inline void add(int x , int y , int z) { to[++cnt] = y , val[cnt] = z , next[cnt] = head[x] , head[x] = cnt; to[++cnt] = x , val[cnt] = 0 , next[cnt] = head[y] , head[y] = cnt; } bool bfs() { int x , i; memset(dis , 0 , sizeof(dis)); while(!q.empty()) q.pop(); dis[s] = 1 , q.push(s); while(!q.empty()) { x = q.front() , q.pop(); for(i = head[x] ; i ; i = next[i]) { if(val[i] && !dis[to[i]]) { dis[to[i]] = dis[x] + 1; if(to[i] == t) return 1; q.push(to[i]); } } } return 0; } int dinic(int x , int low) { if(x == t) return low; int temp = low , i , k; for(i = head[x] ; i ; i = next[i]) { if(val[i] && dis[to[i]] == dis[x] + 1) { k = dinic(to[i] , min(temp , val[i])); if(!k) dis[to[i]] = 0; val[i] -= k , val[i ^ 1] += k; if(!(temp -= k)) break; } } return low - temp; } int main() { int n , i , j , sum = 0; scanf("%d" , &n) , s = 0 , t = n + 1; for(i = 1 ; i <= n ; i ++ ) { scanf("%d%d%d%d" , &x[i] , &y[i] , &r[i] , &v[i]); if(v[i] > 0) add(s , i , v[i]) , sum += v[i]; else add(i , t , -v[i]); } for(i = 1 ; i <= n ; i ++ ) for(j = 1 ; j <= n ; j ++ ) if(i != j && (x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]) <= r[i] * r[i]) add(i , j , inf); while(bfs()) sum -= dinic(s , inf); printf("%d\n" , sum); return 0; }