zoukankan      html  css  js  c++  java
  • 洛谷.4252.[NOI2006]聪明的导游(提答 直径 随机化)

    题目链接

    随机化 暴力:
    随便从一个点开始DFS,每次从之前得到的f[i]最大的子节点开始DFS。f[i]为从i开始(之前)能得到的最大答案。
    要注意的是f[i]应当有机会从更小的答案更新,
    9.10求直径。
    就82分了。

    本来想的SPFA啥的也不对。。正解思路是这的

    #include <ctime>
    #include <cstdio>
    #include <cctype>
    #include <vector>
    #include <cstring>
    #include <algorithm>
    #define gc() getchar()
    const int N=10005,M=2e4+5;
    
    int n,m,Max,pos,A[N],Enum,H[N],nxt[M],to[M],f[N];
    bool vis[N];
    std::vector<int> ans,tmp,ANS;
    
    inline int read()
    {
    	int now=0;register char c=gc();
    	for(;!isdigit(c);c=gc());
    	for(;isdigit(c);now=now*10+c-'0',c=gc());
    	return now;
    }
    inline void AddEdge(int u,int v){
    	to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
    	to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum;
    }
    void DFS(int x)
    {
    	vis[x]=1, tmp.push_back(x);
    	int way=0;
    	for(int i=H[x]; i; i=nxt[i])
    		if(!vis[to[i]]&&f[way]<f[to[i]]) way=to[i];
    	if(way) DFS(way);
    }
    void Solve()
    {
    	memset(f,0,sizeof f);
    	for(int t=500; t; --t)
    		for(int i=1; i<=n; ++i)
    		{
    			memset(vis,0,sizeof vis), tmp.clear(), DFS(i);
    			if(tmp.size()>ans.size()) ans=tmp;
    //			if(tmp.size()>f[i])  f[i]=tmp.size();
    			if(tmp.size()>f[i]||rand()%15==0)  f[i]=tmp.size();
    		}
    	if(ans.size()>ANS.size()) ANS=ans;
    }
    namespace Spec
    {
    	int Max,v,pre[N];
    
    	void DFS(int x,int f,int d)
    	{
    		if(d>Max) Max=d, v=x;
    		for(int i=H[x]; i; i=nxt[i])
    			if(to[i]!=f) pre[to[i]]=x, DFS(to[i],x,d+1);
    	}
    	void Solve()
    	{
    		Max=0, DFS(1,1,1);
    		int u=v;
    		Max=0, DFS(v,v,1);
    		printf("%d
    %d
    ",Max,v);
    		while(v!=u) printf("%d
    ",v=pre[v]);
    	}
    }
    
    int main()
    {
    //	freopen("guide.in","r",stdin);
    //	freopen("guide.out","w",stdout);
    
    	srand(time(NULL));
    	n=read(),m=read();
    	for(int i=1; i<=m; ++i) AddEdge(read(),read());
    //	Spec::Solve(); return 0;//9.10
    	for(int T=1; T<=15; ++T) Solve();
    	printf("%d
    ",ANS.size());
    	for(int i=0; i<ANS.size(); ++i) printf("%d
    ",ANS[i]);
    
    	return 0;
    }
    

    正解:
    可以先随机生成一棵树,然后去求它的直径。
    想要这棵树的直径尽量大,它上面的点的度数应尽量小(要不大概就是很多分叉那样)。
    枚举/随机一个根节点x,从它开始DFS,每次优先走度数最小的点v(注意把它相连的点的度数-1),连树上的边x->v。
    DFS完这个度数最小的点v后,可以从x再走其它未访问的点,扩展树。

    #include <ctime>
    #include <cstdio>
    #include <cctype>
    #include <cstring>
    #include <algorithm>
    #define gc() getchar()
    const int N=10005,M=2e4+5;
    
    int n,m,Max,V,pre[N],Enum,H[N],nxt[M],to[M],_Enum,_H[N],_nxt[M],_to[M],Dgr[N],dgr[N],ans[N];
    bool vis[N];
    
    inline int read()
    {
    	int now=0;register char c=gc();
    	for(;!isdigit(c);c=gc());
    	for(;isdigit(c);now=now*10+c-'0',c=gc());
    	return now;
    }
    inline void AddEdge(int u,int v){
    	++Dgr[u], to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
    	++Dgr[v], to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum;
    }
    inline void AddTree(int u,int v){
    	_to[++_Enum]=v, _nxt[_Enum]=_H[u], _H[u]=_Enum;
    	_to[++_Enum]=u, _nxt[_Enum]=_H[v], _H[v]=_Enum;
    }
    void Build(int x)
    {
    	vis[x]=1;
    	int way=0;
    	for(int i=H[x]; i; i=nxt[i]) --dgr[to[i]];
    	for(int i=H[x]; i; i=nxt[i])
    		if(!vis[to[i]]&&(dgr[way]>dgr[to[i]]||(dgr[way]==dgr[to[i]]&&rand()&1))) way=to[i];
    //		if(!vis[to[i]]&&dgr[way]>dgr[to[i]]) way=to[i];//略不对 
    	if(way) AddTree(x,way), Build(way);
    	for(int i=H[x]; i; i=nxt[i])
    		if(!vis[to[i]]) AddTree(x,to[i]), Build(to[i]);
    }
    void DFS(int x,int f,int d)
    {
    	if(d>Max) Max=d, V=x;
    	for(int i=_H[x]; i; i=_nxt[i])
    		if(_to[i]!=f) pre[_to[i]]=x, DFS(_to[i],x,d+1);
    }
    void Solve(int i)
    {
    	_Enum=0, memset(_H,0,sizeof _H);
    	memset(vis,0,sizeof vis), memcpy(dgr,Dgr,sizeof dgr), Build(i);
    	Max=0, DFS(i,i,1);
    	int u=V;
    	Max=0, DFS(u,u,1);
    	if(Max>ans[0]){
    		ans[ans[0]=Max]=V;
    		while(V!=u) ans[--ans[0]]=V=pre[V];
    		ans[0]=Max;
    	}
    }
    
    int main()
    {
    //	freopen("guide.in","r",stdin);
    //	freopen("guide.out","w",stdout);
    
    	srand(time(NULL));
    	n=read(),m=read();
    	for(int i=1; i<=m; ++i) AddEdge(read(),read());
    	Dgr[0]=1000000;
    	for(int T=10000; T; --T) Solve(rand()%n+1);//还是要多次 
    //	for(int i=1; i<=n; ++i) Solve(i);
    	printf("%d
    ",ans[0]);
    	for(int i=1; i<=ans[0]; ++i) printf("%d
    ",ans[i]);
    
    	return 0;
    }
    
  • 相关阅读:
    Java 使用Calendar类输出指定年份和月份的日历
    ioc aop
    多线程下单例模式:懒加载(延迟加载)和即时加载
    Java生产环境下性能监控与调优详解
    springboot + zipkin + mysql
    springboot + zipkin(brave-okhttp实现)
    springboot启动方式
    OpenResty实现限流的几种方式
    RocketMQ核心技术精讲与高并发抗压实战
    codis 使用
  • 原文地址:https://www.cnblogs.com/SovietPower/p/8981733.html
Copyright © 2011-2022 走看看