【bzoj3291】Alice与能源计划 模拟费用流+二分图最大匹配
题目描述
在梦境中,Alice来到了火星。不知为何,转眼间Alice被任命为火星能源部长,并立刻面临着一个严峻的考验。
为了方便,我们可以将火星抽象成平面,并建立平面直角坐标系。火星上一共有N个居民点。每个居民点认为是平面上的一个点。第i个居民点的坐标为(Xi, Yi),对能源的需求量为Poweri。每个居民点消耗的能源由它附近的发电站提供。由于技术原因,一个居民点消耗的所有能源必须来自同一座发电站。自人类移民火星之初,政府就有一个规模宏大的发电站建设计划。按照这个计划,政府将在火星上建立M座发电站,这M座发电站将是火星居民的全部能量来源。其中,第i座发电站的坐标为(xi, yi),产生能量的上限值为Limiti,建设费用为Pricei。同样由于技术原因,第i座发电站只能为与它的距离不超过Ri的居民点提供能源。然而,由于政府的财政状况一直十分紧张,截至目前,这M座发电站中只有少量建成并投入使用,多数的发电站尚未开始建设。
Alice的任务是修改这个计划,使得它更符合实际情况。根据新的规定,一座发电站产生的所有能源必须提供给同一个居民点。Alice知道这个规定意味着N个居民点消耗的能源将分别由N座不同的发电站提供,而且为第i个居民点提供能源的那座发电站的Limit值一定不小于Poweri。
Alice需要在原计划的M座发电站中选择恰好N座发电站,并完全放弃剩余的M - N座发电站,使得这N座发电站能够满足N个居民点的需要。对于一个可行的方案,设方案中选择的N座发电站构成集合S,而已经建成的发电站构成集合T,那么定义这个方案的代价为
即,一个方案的代价等于被选择的且尚未建成的发电站的建设费用之和加上没有被选择的且已经建成的发电站的建设费用之和。
在所有可行方案中,你必须帮助Alice找到代价最小的方案,并将选择的N座发电站按编号从小到大的顺序输出。如果代价最小的方案不唯一,则输出其中字典序最小的方案。
注意,输入文件包含多组测试数据。
输入
第一行包含一个正整数T,表示有T组测试数据。接下来依次是T组测试数据。
每组测试数据的第一行包含两个整数N、M。
接下来N行,每行3个整数:Xi、Yi、Poweri。
再接下来M行,每行6个整数:xi、 yi、Limiti、Pricei、Ri、Finishedi。若Finishedi = 1,表示第i座发电站已经建成;否则Finishedi = 0,表示第i座发电站尚未开始建设。
输出
若存在可行方案则输出两行。第一行为一个整数,表示最小代价;第二行是若干个递增的整数,表示字典序最小的最优方案选择的发电站的编号。
若不存在可行方案,则仅输出一行为一个整数-1。
样例输入
4
1 1
4 4 1
8 7 1 2 5 1
2 3
0 0 3
2 0 2
1 1 5 1 3 0
1 0 5 1 1 1
3 0 5 1 3 0
2 3
0 0 3
2 0 2
1 1 2 0 3 0
1 0 1 0 1 1
3 0 3 0 2 0
2 3
0 0 3
2 0 2
1 1 4 2 2 0
1 0 2 9 1 1
3 0 5 4 2 1
样例输出
0
1
1
1 2
-1
6
1 2
题解
模拟费用流+二分图最大匹配
首先一眼看出本题是一道费用流题。
然而发现这里有两种发电站:已建设的和未建设的,他们的代价类型不同。因此需要统一代价。具体方法很容易想到:把已建设的代价先计入到答案中,然后把代价去相反数,按照未建设处理。
建图方法:S->能源站,容量为1,费用为代价;每个发电站->能够供给的居民点,容量为1,费用为0;每个居民点->T,容量为1,费用为0。
然而这样直接跑费用流会TLE。但是我们可以模拟这个过程。
费用流的过程是先找到一条费用最小的增广路,把边反向,重复这个过程。而本题的费用只出现于S->能源站的边上,所以我们可以按照费用从小到大贪心,看是否能够找到增广路。
显然,这是一个二分图最大匹配问题。于是按照费用从小到大排序后跑匈牙利算法即可。
为什么加了所有能够想到的优化跑得还是没有rank 2的同学快= =
#include <cstdio> #include <cstring> #include <algorithm> #define N 510 using namespace std; struct data { int x , y , l , p , r , id; bool operator<(const data &a)const {return p == a.p ? id < a.id : p < a.p;} }a[N]; int px[N] , py[N] , pp[N] , head[N] , to[N * N] , next[N * N] , cnt , from[N] , now , vis[N] , pa[N]; inline char nc() { static char buf[100000] , *p1 , *p2; return p1 == p2 && (p2 = (p1 = buf) + fread(buf , 1 , 100000 , stdin) , p1 == p2) ? EOF : *p1 ++ ; } inline int read() { int ret = 0; char ch = nc(); while(ch < '0' || ch > '9') ch = nc(); while(ch >= '0' && ch <= '9') ret = (ret << 3) + (ret << 1) + ch - '0' , ch = nc(); return ret; } void add(int x , int y) { to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt; } bool dfs(int x) { int i; for(i = head[x] ; i ; i = next[i]) { if(vis[to[i]] != now) { vis[to[i]] = now; if(!from[to[i]] || dfs(from[to[i]])) { from[to[i]] = x; return true; } } } return false; } int main() { int T = read(); while(T -- ) { memset(head , 0 , sizeof(head)) , memset(from , 0 , sizeof(from)) , memset(vis , 0 , sizeof(vis)) , memset(pa , 0 , sizeof(pa)) , cnt = 0; int n = read() , m = read() , i , j , opt , ans = 0 , sum = 0; for(i = 1 ; i <= n ; i ++ ) px[i] = read() , py[i] = read() , pp[i] = read(); for(i = 1 ; i <= m ; i ++ ) { a[i].x = read() , a[i].y = read() , a[i].l = read() , a[i].p = read() , a[i].r = read() , opt = read() , a[i].id = i; if(opt) ans += a[i].p , a[i].p = -a[i].p; } sort(a + 1 , a + m + 1); for(i = 1 ; i <= m ; i ++ ) for(j = 1 ; j <= n ; j ++ ) if(a[i].l >= pp[j] && (a[i].x - px[j]) * (a[i].x - px[j]) + (a[i].y - py[j]) * (a[i].y - py[j]) <= a[i].r * a[i].r) add(i , j); for(now = 1 ; now <= m && sum <= n ; now ++ ) if(dfs(now)) ans += a[now].p , sum ++ , pa[a[now].id] = 1; if(sum < n) puts("-1"); else { printf("%d\n" , ans); for(i = 1 ; i <= m ; i ++ ) if(pa[i]) printf("%d%c" , i , sum > 1 ? ' ' : '\n') , sum -- ; } } return 0; }