【图论】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在点上的略。。程序如下:

View Code
 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]时候。代码如下:

View Code
 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的题,我们仔细考虑可以发现当使用图的绝对中心,作为根生成的最短距离树,必然直径最小,可以考虑图绝对中心定义。。所以两个题目没有本质区别

View Code
 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 }

 

 

posted @ 2013-03-05 17:53  lzqxh  阅读(1206)  评论(8编辑  收藏  举报