ZOJ 3820 Building Fire Stations / 牡丹江现场赛B题

方法:

 先找出树上最长的链,最终的两点一定是在最长的链上(这个很容易看出来),至于怎么找最长的链是modiz教我的。。。。。

先以树上任意一点为根节点找出最深的点pre,再以pre为根节点找出最深的点v,那么最长的链就是pre-v这条链(感觉有点吊,完全想不到啊)

找出最长的链后,找出树的中点,然后从树的中间切开变成两个子树,求这两个子树的中点即可((⊙_⊙)?这样为什么会对。。。)

遍历树的时候,首先用的dfs,发现溢出了,必须要用bfs遍历

 

代码:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
#define maxn 200010

struct node
{
    int p;
    int depth;
    node(int x, int y):
    p(x),depth(y){}
    node(){}
};

int F[maxn], h[maxn];
int maxd;

vector<int>aa[maxn];
queue<node> q;

void bfs(int &x)
{
    while(!q.empty())
    {
        node N= q.front();
        q.pop();
        //printf("%d\n",N.p);
        if(N.depth> maxd)
        {
            x= N.p;
            maxd= N.depth;
        }
        for(int i= 0; i< aa[N.p].size(); i++)
            if(!h[aa[N.p][i]])
            {
                h[aa[N.p][i]]= 1;
                node M;
                M.p= aa[N.p][i];
                M.depth= N.depth + 1;
                F[M.p]= N.p;
                q.push(M);
            }
    }
}


int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
	int T;
    scanf("%d",&T);
    while(T--)
    {
        int n, a, b;
        scanf("%d",&n);
        for(int i= 1; i<= 200000; i++)
            aa[i].clear();
        for(int i= 1; i<= n-1; i++)
        {
            scanf("%d %d",&a, &b);
            aa[a].push_back(b);
            aa[b].push_back(a);
        }
		if(n== 2)
        {
            printf("0 1 2\n");
            continue;
        }
        int pre, v;
		memset(h, 0, sizeof h);
        h[1]= 1, maxd= 1, pre= 1; //以点1为根节点建树 
        q.push(node(1,1));
        bfs(pre); 
    //    printf("pre=%d\n",pre);
        memset(h, 0, sizeof h);
        memset(F, -1, sizeof F);
        h[pre]= 1, maxd= 1, v= pre;  //以pre点为根节点建树 
        q.push(node(pre, 1));
        bfs(v);
    //    printf("v= %d\n",v);
    //    printf("maxd=%d\n",maxd);
        /*ans= (maxd-2)/4;
        if((maxd-2)%4)
            ans++;*/
        int xx; //整棵树的中点    
        for(int i= v, j= 1; i!= -1; i= F[i], j++)
		//这个链是从pre起,v结束所以一定要这样找中点, 
        //因为我待会删除的边是xx与F[xx]之间的边, 
		//所以不这样遍历会错,下面那种for(int i= ppre, j= maxd/2; i!= -1; i= F[i], j--)是因为子树最长的链的两个中点任取一个都行(假如有两个中点) 
		{
	//	    printf("%d %d\n",i,j);
			if(2*j>= maxd)
            {
                xx= i;
                break;
            }
        }
    //    printf("xx=%d F[xx]=%d\n",xx,F[xx]);    
        for(int i= 0; i< aa[xx].size(); i++)
			if(aa[xx][i]== F[xx])
			{
				aa[xx].erase(aa[xx].begin()+i, aa[xx].begin()+i+1);	                  
        		break;
        	}
		for(int i= 0; i< aa[F[xx]].size(); i++)
        	if(aa[F[xx]][i]== xx)
        	{
				aa[F[xx]].erase(aa[F[xx]].begin()+i, aa[F[xx]].begin()+i+1);
				break;
			}
		//把这棵树分成两棵树求中点		
		int ppre= pre;
		memset(h, 0, sizeof h);
        memset(F, -1, sizeof F);
        h[pre]= 1, maxd= 1;  
        q.push(node(pre, 1));
        bfs(ppre);  //包含pre点的子树
	//	printf("ppre=%d\n",ppre);
		int ans= 0, p1= -1, p2= -1;
		for(int i= ppre, j= maxd/2; i!= -1; i= F[i], j--)
            if(j== 0)
            {
                p1= i;
                break;
            }
        ans= max(ans, maxd/2); //p1为第一颗子树的中点   
        int vv= v;
		memset(h, 0, sizeof h);
        memset(F, -1, sizeof F);
        h[v]= 1, maxd= 1;  
        q.push(node(v, 1));
        bfs(vv);  //包含v点的子树
	//	printf("vv=%d\n",vv);
		for(int i= vv, j= maxd/2; i!= -1; i= F[i], j--)
            if(j== 0)
            {
                p2= i;
                break;
            }  
		ans= max(ans, maxd/2);	      
        printf("%d %d %d\n",ans, p1, p2);
    }
    return 0;
}


 

 

你可能感兴趣的:(ZOJ 3820 Building Fire Stations / 牡丹江现场赛B题)