zoukankan      html  css  js  c++  java
  • 【洛谷4252】[NOI2006] 聪明的导游(提答题)

    点此看题面

    大致题意: 给你一张(n)个点、(m)条边的无向图,让你找出图上的一条不经过重复节点的最长路(提答+(spj))。

    随机化乱搞

    针对这种提答题,我们就要用随机化乱搞(Cptraser大佬说他可以直接(O(n^2))求出答案%%% )。

    首先,我们随机一个节点作为起点

    然后,我们从这个节点出发,每次选择一个还没有访问过度数较小(这可以通过随机化实现) 的相邻节点进行遍历。

    遍历完该节点之后,如果还有没有访问过的相邻节点,就重复上述操作。

    呃,貌似就这么简单?

    当然,随机化算法肯定要循环操作若干次,推荐(5000)次(毕竟它是提答,就算(5000)次没(rand)出答案,还可以继续(rand))。

    有一个细节需要注意:选择一个点之后,要将与这个节点相邻的节点的度数减1。(我就是因为没这样做(rand)整整一个晚上都没(rand)出正确答案,加上这个细节之后秒出答案)

    具体实现还是看代码吧。

    代码

    #include<bits/stdc++.h>
    #define max(x,y) ((x)>(y)?(x):(y))
    #define min(x,y) ((x)<(y)?(x):(y))
    #define uint unsigned int
    #define LL long long
    #define ull unsigned long long
    #define swap(x,y) (x^=y,y^=x,x^=y)
    #define abs(x) ((x)<0?-(x):(x))
    #define INF 1e9
    #define Inc(x,y) ((x+=(y))>=MOD&&(x-=MOD))
    #define ten(x) (((x)<<3)+((x)<<1))
    #define N 10000
    #define add(x,y) (e[++ee].nxt=lnk[x],++deg[e[lnk[x]=ee].to=y])
    #define hl_AK_NOI true
    using namespace std;
    int n,m,ee=0,lnk[N+5],deg[N+5];
    struct edge
    {
    	int to,nxt;
    }e[2*N+5];
    class FIO
    {
    	private:
    		#define Fsize 100000
    		#define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++)
    		#define pc(ch) (FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,FoutSize,stdout),Fout[(FoutSize=0)++]=ch))
    		int f,FoutSize,OutputTop;char ch,Fin[Fsize],*FinNow,*FinEnd,Fout[Fsize],OutputStack[Fsize];
    	public:
    		FIO() {FinNow=FinEnd=Fin;}
    		inline void read(int &x) {x=0,f=1;while(!isdigit(ch=tc())) f=ch^'-'?1:-1;while(x=ten(x)+(ch&15),isdigit(ch=tc()));x*=f;}
    		inline void read_char(char &x) {while(isspace(x=tc()));}
    		inline void read_string(string &x) {x="";while(isspace(ch=tc()));while(x+=ch,!isspace(ch=tc())) if(!~ch) return;}
    		inline void write(int x) {if(!x) return (void)pc('0');if(x<0) pc('-'),x=-x;while(x) OutputStack[++OutputTop]=x%10+48,x/=10;while(OutputTop) pc(OutputStack[OutputTop]),--OutputTop;}
    		inline void write_char(char x) {pc(x);}
    		inline void write_string(string x) {register int i,len=x.length();for(i=0;i<len;++i) pc(x[i]);}
    		inline void end() {fwrite(Fout,1,FoutSize,stdout);}
    }F;
    class Class_RandSolver//听起来很高大上的名字,实际上就是随机化
    {
    	private:
    		int vis[N+5];
    		struct Status
    		{
    			int ans,rt,lst[N+5],dis[N+5];//ans记录最长路径长度,rt记录起点,lst数组记录每个节点的上一个节点(方便输出路径),dis记录起点到达每一个节点的距离
    			inline void Clear() {ans=0,memset(lst,0,sizeof(lst)),memset(dis,0,sizeof(dis));}//清空
    		}NowAns,MaxAns;//NowAns记录当前答案,MaxAns记录最优答案
    		inline void Clear() {NowAns.Clear(),memset(vis,0,sizeof(vis));}//清空
    		inline void dfs(int x)//搜索
    		{
    			register int i,t,flag;vis[x]=1;//标记已访问
    			if(NowAns.dis[x]>NowAns.ans) NowAns.ans=NowAns.dis[x];//更新最长路径长度
    			for(i=lnk[x];i;i=e[i].nxt) --deg[e[i].to];//将与当前节点相邻的节点度数减1
    			while(hl_AK_NOI)//怀着崇高的信仰膜拜hl,这样就可以++RP
    			{
    				for(t=flag=0,i=lnk[x];i;i=e[i].nxt) if(!vis[e[i].to]) flag=1,deg[e[i].to]<deg[t]&&1.0*n/deg[e[i].to]*rand()>=RAND_MAX&&(t=e[i].to);//选择一个没被访问过的度数较小的相邻节点,注意加上一些随机化的因素
    				if(!flag) break;//如果没有没被访问过的相邻节点,就退出循环
    				if(!t) continue;//如果刚才没有找到节点,就重新找一遍
    				NowAns.dis[t]=NowAns.dis[x]+1,NowAns.lst[t]=x,dfs(t);//对这个相邻节点进行dfs
    			}
    			for(i=lnk[x];i;i=e[i].nxt) ++deg[e[i].to];//将刚才减掉的度数加回去
    		}
    	public:
    		Class_RandSolver() {srand(time(NULL)),srand(rand()),srand(rand());}//初始化
    		inline void GetAns()//求解答案
    		{
    			Clear(),NowAns.dis[NowAns.rt=rand()%n+1]=1,dfs(NowAns.rt);//随机一个起点开始dfs
    			if(NowAns.ans>MaxAns.ans) MaxAns=NowAns;//如果得到的答案比最优答案大,更新最优答案
    		}
    		inline void PrintAns()//输出答案
    		{
    			register int i;
    			for(F.write(MaxAns.ans),F.write_char('
    '),i=1;i<=n;++i) if(MaxAns.dis[i]==MaxAns.ans) break;//找到一个终点
    			while(i^MaxAns.rt) F.write(i),F.write_char('
    '),i=MaxAns.lst[i];F.write(MaxAns.rt);//输出路径
    		}
    }RandSolver;
    int main()
    {
        register int i,x,y,ans=0;
        for(F.read(n),F.read(m),i=1;i<=m;++i) F.read(x),F.read(y),add(x,y),add(y,x);
        for(deg[0]=INF,i=1;i<=5000;++i) RandSolver.GetAns();//循环5000次求出答案
        return RandSolver.PrintAns(),F.end(),0;
    }
    
  • 相关阅读:
    设计模式-----简单工厂模式
    LeetCode题解002:两数相加
    LeetCode题解001:两数之和
    异常处理类-Throwable源码详解
    Windows下 gcc/g++的安装与配置
    Windows10下安装解压版MySQL教程
    Windows下Django项目搭建流程
    Linux域名服务DNS
    Linux文件共享服务 FTP,NFS 和 Samba
    操作系统:进程的概念和与程序的区别
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu4252.html
Copyright © 2011-2022 走看看