poj 2391 最大流+二分+floyd
/* 题意:F个区域和P条路径,每个区域有a头牛且能遮蔽b头牛,求其中牛走得最远的路径的最短长度,并且 所有牛都可以进入遮蔽处。 题解:最大流+二分+floyd+拆点; 牛从所在的位置走向遮蔽处必然是走最短路线,因此先用floyd将两两点之间的最短路径先求出,再根据这 个最短路径建立图,源点到每个点加边,权值为牛数a,将区域拆成两个点,并且对相连的区域之间加边, 权值为INF,注意自己和自己也有加上边,就是被这里坑了很久,区域到汇点加边,权值为遮蔽数b;二分 的方法将长于mid的边去除从而建图;然后求出最大流等于牛数目即可。 注意:题目有重边,只记录最小边即可,路径的长度会超过32为,因此用__int64记录路径长度。 */ #include <cstdio> #include <cstring> #include <algorithm> #include <queue> using namespace std; #define EMAX 505000 #define VMAX 500 #define MAX 0x7FFFFFFFFFFFFFF const int INF = 0xFFFFFFF; int head[VMAX],dis[VMAX],cur[VMAX],gap[VMAX],pre[VMAX]; int EN; struct edge { int to; int weight; int next; }e[EMAX]; struct cow { int a,b; }te[VMAX]; __int64 map[205][205]; void insert(int u,int v,int w) { e[EN].to = v; e[EN].weight = w; e[EN].next = head[u]; head[u] = EN++; e[EN].to = u; e[EN].weight = 0; e[EN].next = head[v]; head[v] = EN++; } int sap(int s,int t, int n)//sap求最大流模版 { memset(dis,0,sizeof(dis)); memset(gap,0,sizeof(gap)); for(int i=0; i<=n; i++) cur[i] = head[i]; int u = s; pre[s] = s; int ret = 0; int temp = -1; gap[0] = n; bool flag; while(dis[s] < n) { flag = false; for(int &i = cur[u]; i != -1; i = e[i].next) { int v = e[i].to; if(e[i].weight && dis[u] == dis[v] + 1) { if (temp == -1 || temp>e[i].weight) temp = e[i].weight; pre[v] = u; u = v; if(v == t) { ret += temp; for(u = pre[u];v != s;v = u,u = pre[u]) { e[cur[u]].weight -= temp; e[cur[u]^1].weight += temp; } temp = -1; } flag = true; break; } } if (flag) continue; int mindis = n; for(int i = head[u]; i != -1 ; i = e[i].next) { int v = e[i].to; if(e[i].weight && mindis > dis[v]) { cur[u] = i; mindis = dis[v]; } } gap[dis[u]]--; if( gap[dis[u]] == 0) break; dis[u] = mindis+1; gap[dis[u]]++; u = pre[u]; } return ret; } void floyd(int n) { for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) if (MAX != map[j][i]) for(int k=1; k<=n; k++) if (map[j][i] + map[i][k] < map[j][k]) map[j][k] = map[j][i] + map[i][k]; } void build(int f,__int64 mid)//建图 { memset(head,-1,sizeof(head)); EN = 0; for(int i=1; i<=f; i++) { insert(0,i,te[i].a); insert(f+i,2*f+1,te[i].b); } for(int i=1; i<=f; i++) for(int j=1; j<=f; j++) if (map[i][j]<=mid)//去除大于mid的边 insert(i,f+j,INF); } __int64 bin(int f, int sum)//二分 { __int64 left=0,right=MAX,mid; while (left < right) { mid = (left+right)/2; build(f,mid); if (sap(0,2*f+1,2*f+2) < sum) left = mid+1; else right = mid; } if (right == MAX) return -1; else return right; } int main(void) { int f,p,a,b,l; while (~scanf("%d%d",&f,&p)) { int sum = 0; for(int i=1; i<=f; i++) for(int j=1; j<=f; j++) { if (i != j) map[i][j] = MAX; else map[i][j] = 0; } for(int i=1; i<=f; i++) { scanf("%d%d",&te[i].a,&te[i].b); sum += te[i].a;//求牛的总数 } for(int i=1; i<=p; i++) { scanf("%d%d%d",&a,&b,&l); if (map[a][b] > l) map[a][b] = map[b][a] = l; } floyd(f); printf("%I64d\n",bin(f,sum)); } return 0; }