【图论】CF266D - BerDonalds && Spoj PT07C
http://www.spoj.com/problems/PT07C/
http://codeforces.com/problemset/problem/266/D
两道题基本意思思路,一起讲一下题解。cf那题题意很简单,求图的绝对中心,使得其到图中最远的点的距离最小,注意该中心可以存在图的边上。
作为一个懒得动脑的人(汗~)意识到答案,必然是整数或者1/2之后就决定用2分处理。。简单证明如下,设点X为图的中心,则必然存在图上同时两点u,v是最远点,即有dis[X][u]==dis[X][v],否则,我们可以将X向最远点移近一点点,构造出更优的解。。。因此ansL = (u->X->v路径长度)/2 所以ans必然是1/2倍数..。
二分答案之后,如果X在边u,v上,则假设X于u距离为a,则于v距离为L-a..故此时ansL = max( min( dis[u][i]+x , dis[v][i]+L-x) ); 我们如果已知ansL的大小可以通过n个方程min( dis[u][i]+x , dis[v][i]+L-x)<=ansL ;来得到n个x的合法区间,通过区间合并判断是否存在1个点被n个区间同时覆盖,即可判断ansL是否合法。。X在点上的略。。程序如下:
1 //By Lin 2 #include<cstdio> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #include<set> 7 #include<map> 8 #include<queue> 9 10 #define sqr(x) ((x)*(x)) 11 #define Rep(i,n) for(int i = 0; i<n; i++) 12 #define foreach(i,n) for( __typeof(n.begin()) i = n.begin(); i!=n.end(); i++) 13 #define X first 14 #define Y second 15 #define mp(x,y) make_pair(x,y) 16 17 using namespace std; 18 typedef long long LL; 19 typedef pair<int,int> pii; 20 21 #define N 205 22 #define M 40010 23 24 int n,m; 25 int g[N][N]; 26 int input[N*N][3]; 27 28 void check(int &x,int y){ 29 if ( x == -1 || x > y ) x = y; 30 } 31 32 bool pan(int a,int b,int L,int ans){ 33 map<int,int> mm; 34 mm.clear(); 35 Rep(i,n){ 36 int l , r; 37 r = min( g[b][i]+L-g[a][i] , ans-2*g[a][i] ) ; 38 l = max( g[b][i]+L-g[a][i] , 2*g[b][i]+2*L-ans); 39 // printf("%d %d\n" , r , l ); 40 if ( l == r ) l++; 41 mm[0]++; 42 mm[r+1]--; 43 mm[l]++; 44 mm[L*2+1]--; 45 } 46 int last = 0; 47 foreach( it , mm ) { 48 // printf("%d %d\n" , it->X , it->Y ); 49 last += it->Y; 50 if ( last >= n ) return true; 51 } 52 return false; 53 } 54 55 int main(){ 56 scanf("%d%d", &n, &m ); 57 memset( g , -1 , sizeof(g) ); 58 Rep(i,n) g[i][i] = 0; 59 Rep(i,m){ 60 scanf("%d%d%d", &input[i][0], &input[i][1] , &input[i][2] ); 61 input[i][0]-- , input[i][1]--; 62 g[input[i][0]][input[i][1]] = input[i][2]; 63 g[input[i][1]][input[i][0]] = input[i][2]; 64 } 65 Rep(k,n) Rep(i,n) Rep(j,n) 66 if ( g[i][k] != -1 && g[j][k] != -1 ) check( g[i][j] , g[i][k]+g[k][j] ); 67 int ans = 1e9; 68 Rep(i,n){ 69 int now = 0; 70 Rep(j,n) now = max( now , g[i][j] ); 71 ans = min( ans , now ); 72 } 73 ans *= 2; 74 Rep(i,m){ 75 int a = input[i][0] , b = input[i][1], L1 = 0 , L2 = 0; 76 int L = 0 , R = ans; 77 if ( !pan(a,b,input[i][2],ans) ) continue; 78 while ( L <= R ){ 79 int mid = (L+R)/2; 80 if ( pan(a,b,input[i][2],mid) ) R = mid-1 , ans = mid; 81 else L = mid+1; 82 } 83 } 84 printf("%.10f\n" , ans/2.0 ); 85 return 0; 86 }
最后AC时候看到于其他人运行时间差距非常到,在网上找到关于图绝对中心的O(n^3)解法,主要是通过对ansL = max( min( dis[u][i]+x , dis[v][i]+L-x) )的分析来提速。方法主要为图解法。这里贴一下别人的图=。=~
单个min( dis[u][i]+x , dis[v][i]+L-x)在图中表示为一条折线,取max之后ansL变化就变成图中实线,则答案必然产生在L1,L2,L3,L4,L5上,由于斜率一致,我们可以先根据dis[u][i]排序,再考虑何时出现交点,必然是当dis[v][j] > dis[v][i]时候。代码如下:
1 //By Lin 2 #include<cstdio> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #include<set> 7 #include<map> 8 #include<queue> 9 10 #define sqr(x) ((x)*(x)) 11 #define Rep(i,n) for(int i = 0; i<n; i++) 12 #define foreach(i,n) for( __typeof(n.begin()) i = n.begin(); i!=n.end(); i++) 13 #define X first 14 #define Y second 15 #define mp(x,y) make_pair(x,y) 16 17 using namespace std; 18 typedef long long LL; 19 typedef pair<int,int> pii; 20 21 #define N 205 22 #define M 40010 23 24 int n,m; 25 int g[N][N],rank[N][N]; 26 int input[N*N][3]; 27 28 void check(int &x,int y){ 29 if ( x == -1 || x > y ) x = y; 30 } 31 32 int main(){ 33 scanf("%d%d", &n, &m ); 34 memset( g , -1 , sizeof(g) ); 35 Rep(i,n) g[i][i] = 0; 36 Rep(i,m){ 37 scanf("%d%d%d", &input[i][0], &input[i][1] , &input[i][2] ); 38 input[i][0]-- , input[i][1]--; 39 g[input[i][0]][input[i][1]] = input[i][2]; 40 g[input[i][1]][input[i][0]] = input[i][2]; 41 } 42 Rep(k,n) Rep(i,n) Rep(j,n) 43 if ( g[i][k] != -1 && g[j][k] != -1 ) check( g[i][j] , g[i][k]+g[k][j] ); 44 int ans = -1; 45 Rep(i,n){ 46 Rep(j,n) rank[i][j] = j; 47 Rep(j,n) 48 for (int k = j+1; k<n; k++) 49 if ( g[i][rank[i][j]] < g[i][rank[i][k]] ) swap( rank[i][j] , rank[i][k] ); 50 check( ans , g[i][rank[i][0]]*2 ); 51 } 52 Rep(t,m){ 53 int a = input[t][0] , b = input[t][1] , L = input[t][2]; 54 for (int i = 0, j = 0; i<n; ) { 55 for ( j = i+1; j<n && g[b][rank[a][j]] <= g[b][rank[a][i]] ; j++); 56 if ( j == n ) break; 57 if ( g[b][rank[a][j]] > g[b][rank[a][i]] ) 58 check( ans , g[a][rank[a][j]]+g[b][rank[a][i]]+L ); 59 i = j; 60 } 61 } 62 printf("%.10f\n" , ans*0.5 ); 63 return 0; 64 }
最后关于spoj的题,我们仔细考虑可以发现当使用图的绝对中心,作为根生成的最短距离树,必然直径最小,可以考虑图绝对中心定义。。所以两个题目没有本质区别
1 //By Lin 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 6 #define N 205 7 #define M 20010 8 #define Rep(i,n) for(int i = 0; i<(n); i++) 9 using namespace std; 10 11 int ecnt; 12 struct Edge{ 13 int to,w,num; 14 Edge *next; 15 }*mat[N],edges[M*2]; 16 void link(int x,int to,int w){ 17 edges[ecnt].to = to; 18 edges[ecnt].w = w; 19 edges[ecnt].num = ecnt/2; 20 edges[ecnt].next = mat[x]; 21 mat[x] = &edges[ecnt++]; 22 } 23 24 int n,m; 25 int input[M][3]; 26 int g[N][N],from[N],dis[N],rank[N][N]; 27 int ans_p[N]; 28 29 bool check(int &x,int y){ 30 if ( x == -1 || x > y ) { x= y; return true; } 31 return false; 32 } 33 34 int main(){ 35 scanf("%d%d", &n, &m ); 36 memset( g , -1 , sizeof(g) ); 37 Rep(i,n) g[i][i] = 0; 38 Rep(i,m) { 39 scanf("%d%d%d", &input[i][0], &input[i][1], &input[i][2] ); 40 input[i][2]*=2; 41 check( g[--input[i][0]][--input[i][1]] , input[i][2]); 42 check( g[input[i][1]][input[i][0]] , input[i][2] ); 43 link( input[i][0] ,input[i][1] ,input[i][2] ); 44 link( input[i][1] ,input[i][0] ,input[i][2] ); 45 } 46 Rep(k,n) Rep(i,n) Rep(j,n) 47 if ( g[i][k] != -1 && g[k][j] != -1 ) check( g[i][j] , g[i][k]+g[k][j] ); 48 Rep(i,n){ 49 Rep(j,n) rank[i][j] = j; 50 Rep(j,n) for(int k = j+1; k<n; k++) 51 if ( g[i][rank[i][j]] < g[i][rank[i][k]] ) swap( rank[i][j] , rank[i][k] ); 52 } 53 int ansL = -1,p = -1 , q = -1 , x = 0 , y = 0; 54 Rep(t,m){ 55 int a = input[t][0] , b = input[t][1], L = input[t][2]; 56 for(int i = 0 , j; i<n; i = j ){ 57 for( j = i+1; j<n && g[b][rank[a][j]] <= g[b][rank[a][i]]; j++); 58 if ( j == n ) break; 59 if ( check( ansL , L+g[a][rank[a][j]]+g[b][rank[a][i]] ) ){ 60 p = a , q = b; 61 x = (L+g[a][rank[a][j]]+g[b][rank[a][i]])/2-g[a][rank[a][j]]; 62 y = L-x; 63 ans_p[0] = t; 64 from[a] = from[b] = -1; 65 } 66 } 67 } 68 Rep(i,n) 69 if ( check( ansL , g[i][rank[i][0]]*2 ) ){ 70 p = i , q = -1 , x = 0; 71 } 72 73 memset( dis , -1 , sizeof(dis) ); 74 dis[p] = x; 75 from[p] = -2; 76 int cnt = 0; 77 if ( q != -1 ) dis[q] = y, cnt = 1 , from[q] = -2; 78 while ( true ) { 79 int i = -1; 80 Rep(k,n) 81 if ( dis[k] >= 0 && ( i == -1 || dis[i] > dis[k] ) ) i = k; 82 if ( i == -1 ) break; 83 if ( from[i] >= 0 ) ans_p[cnt++] = from[i]; 84 for ( Edge *p = mat[i]; p ; p = p->next ){ 85 int to = p->to; 86 if ( dis[to] == -2 ) continue; 87 if ( check( dis[to] , dis[i]+p->w ) ) from[to] = p->num; 88 } 89 dis[i] = -2; 90 } 91 printf("%d\n" , ansL/2 ); 92 Rep(i,n-1) printf("%d %d\n" , input[ans_p[i]][0]+1 , input[ans_p[i]][1]+1 ); 93 return 0; 94 }