【树的直径】ZJOI2012day1[旅游]题解

题目概述

给出一个凸n边形(n个节点),有n-2个城市(三角形),这n-2个城市将这个凸n边形三角剖分。求连接两个节点穿过最多的城市数量(只要和城市的边有两个交点就算穿过)。
这里写图片描述

解题报告

相邻两个城市可以直接互通,那么也就可以连一条边,连接完毕之后不会成环,原因可以yy出来,如图:
这里写图片描述
i想要绕回来,就会出现上图的情况:一个点在里面,这是不可能的。那么知道了这一点之后,连边之后的图就是一棵无根树,显然,无根树中最长的链就是最优解。

但是这样好像完全违背了题目里的只连一条边,实际上没关系,因为:
这里写图片描述
实际处理和题意中处理的是等价的,因为连接一条边穿过城市和不停连接城市都会经过城市边两次(然而还是yy,:P)。

至于最长链,就是树的直径,两次bfs即可。

示例程序

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=200000;

int n,num,E,where,son[2*maxn+5],nxt[2*maxn+5],lnk[maxn+5],que[maxn+5],dis[maxn+5];
bool vis[maxn+5];
struct Edge //边的结构体
{
    int x,y,id;
    bool operator < (const Edge &a) const {return x<a.x||x==a.x&&y<a.y;}
};
Edge e[3*maxn];

bool Eoln(char ch) {return ch==10||ch==13||ch==EOF;}
int readi(int &x) //读入优化
{
    int tot=0,f=1;
    char ch=getchar();if (ch==EOF) return EOF;
    while ('9'<ch||ch<'0') {if (ch=='-') f=-f;ch=getchar();}
    while ('0'<=ch&&ch<='9') tot=tot*10+ch-48,ch=getchar();
    x=tot*f;
    return Eoln(ch);
}
int Bfs(int ro) //bfs
{
    memset(vis,0,sizeof(vis));
    int Head=0,Tail=0,MAX=0;que[++Tail]=ro;dis[Tail]=1;
    while (Head!=Tail)
    {
        int x=que[++Head];
        for (int j=lnk[x];j;j=nxt[j])
            if (!vis[son[j]])
            {
                que[++Tail]=son[j];vis[son[j]]=true;dis[Tail]=dis[Head]+1;
                if (dis[Tail]>MAX) MAX=dis[Tail],where=son[j]; //where表示这次bfs能达到的最远的点
            }
    }
    return MAX;
}
void Add(int x,int y) {son[++E]=y;nxt[E]=lnk[x];lnk[x]=E;}
int main()
{
    freopen("journey.in","r",stdin);
    freopen("journey.out","w",stdout);
    readi(n);
    for (int i=1;i<=n-2;i++)
    {
        int x,y,z;readi(x);readi(y);readi(z);
        if (x>y) swap(x,y);if (x>z) swap(x,z);if (y>z) swap(y,z); //先保证x,y,z升序
        e[++num]=(Edge){x,y,i};e[++num]=(Edge){x,z,i};e[++num]=(Edge){y,z,i}; //建立这个城市的三条边
    }
    sort(e+1,e+1+num); //排序让相同的边凑在一起,相同的边就是相邻城市的公共边
    for (int i=1;i<=num-1;i++) if (e[i].x==e[i+1].x&&e[i].y==e[i+1].y) Add(e[i].id,e[i+1].id),Add(e[i+1].id,e[i].id); //建立城市之间的双向边
    Bfs(1); //求树的直径,先随便抓出一个点,最远的where就是第二次bfs的起点
    printf("%d\n",Bfs(where)); //第二次bfs就是答案
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值