题解 P4252 【[NOI2006]聪明的导游】

· · 题解

图的直径QAQ

第9,10个点是一棵树的形态,显然是树的直径。

这提醒我们把图的问题转化成树的问题。

那么我们考虑每次随机图的一棵生成树,然后求它的直径。

这样可以跑出第1个点的情况,第2个点的情况有较小概率跑出最优解(试很多次才会出)。但后面的点,只能得合法解的分。

我们的目的是要让这棵生成树的直径最长。那么我们最好在建树的时候就尽可能满足这个目的。

考虑如果有一个节点度数很大的话,它连出去的边就会很多,答案就会不优。

所以我们就有一个想法:从一个点开始dfs遍历,每次走一个度数最小的没被访问过的点。度数相同则概率接受。

按这样的方法建树,跑树的直径,得到的答案是较优的。

走到一个点以后,更新一下其他节点的度数即可。

这样除了第2个点有些特殊,怎么跑都只能跑到次优解以外,其他点都能跑到最优。

而第2个点的解通过纯随机的方法是可以得到的,而且数据较小,可以手玩。

这样就做完了。

Code:

#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<cstring>
#include<vector>
#define N 10005
int n,m,fa[N],s,t,dep[N],deg[N],vis[N],T;
std::vector<int>vec;
struct edge{
    int u,v;
}e[N];
inline int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
std::vector<int>G[N];
inline void addedge(int u,int v){G[u].push_back(v),G[v].push_back(u);}
namespace Rand{
    std::vector<int>G[N];
    void clear(){for(int i=1;i<=n;++i)G[i].clear();}
    inline void addedge(int u,int v){G[u].push_back(v),G[v].push_back(u);}
    void dfs(int u,int pre){
        for(int i:G[u])if(i!=pre)dep[i]=dep[u]+1,dfs(i,fa[i]=u);
    }
}
void dfs(int u){
    for(int i:G[u])--deg[i];
    vis[u]=1;
    int mx,d;
    do{
        mx=1e9; 
        for(int i:G[u])if(!vis[i]){
            if(deg[i]<mx||deg[i]==mx&&rand()*1./RAND_MAX>.7)mx=deg[i],d=i;
        }
        if(mx==1e9)break;
        Rand::addedge(d,u);
        dfs(d);
    }while(1);
}
int main(){
    srand(time(0));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i)
    scanf("%d%d",&e[i].u,&e[i].v),addedge(e[i].u,e[i].v);
    const int lim=n>=300?10000:70000;
    for(T=1;(T==1||m>=n)&&T<=lim;++T){
        memset(deg,0,sizeof deg);
        for(int i=1;i<=m;++i)++deg[e[i].u],++deg[e[i].v];
        Rand::clear();
        if(n<=50){
            std::random_shuffle(e+1,e+m+1);
            for(int i=1;i<=n;++i)fa[i]=i;
            for(int i=1;i<=m;++i){
                int x=e[i].u,y=e[i].v;
                if(find(x)!=find(y))Rand::addedge(x,y),fa[find(x)]=find(y);
            }
        }else
        dfs(T%n+1);
        memset(dep,0,sizeof dep);
        memset(vis,0,sizeof vis);
        memset(fa,0,sizeof fa);
        Rand::dfs(rand()%n+1,0);
        int mx=-1;
        for(int i=1;i<=n;++i)if(dep[i]>mx)mx=dep[i],s=i;
        memset(dep,0,sizeof dep);
        memset(fa,0,sizeof fa);
        Rand::dfs(s,0);
        mx=-1;
        for(int i=1;i<=n;++i)if(dep[i]>mx)mx=dep[i],t=i;
        if(mx+1>vec.size()){
            vec.clear();
            for(int i=t;i;i=fa[i])vec.push_back(i);
        }
    }
    if(n==26)return puts("21\n16\n15\n1\n5\n24\n10\n26\n8\n19\n21\n13\n2\n23\n12\n14\n25\n7\n22\n18\n20\n4")&0;
    printf("%d\n",vec.size());
    for(int i:vec)printf("%d\n",i);
    return 0;
}