[NOIp2018提高組]旅行
阿新 • • 發佈:2018-11-15
[NOIp2018提高組]旅行:
題目大意:
一個\(n(n\le5000)\)個點,\(m(m\le n)\)條邊的連通圖。可以從任意一個點出發,前往任意一個相鄰的未訪問的結點,或沿著第一次來這個點的邊返回。需要遍歷每一個點。沒經過一個新的結點,就將這個結點寫下來。最終可以得到一個序列。求字典序最小的序列。
思路:
對於樹的情況,顯然從\(1\)出發,每次從字典序最小的相鄰結點DFS即可。
對於有環的情況,由於環只有一個,我們可以將環找出來,列舉刪掉環上的每一條邊,然後按樹的情況求解即可。
時間複雜度\(\mathcal O(n^2)\)。
原始碼:
#include<set> #include<stack> #include<cstdio> #include<cctype> #include<vector> #include<climits> #include<algorithm> inline int getint() { register char ch; while(!isdigit(ch=getchar())); register int x=ch^'0'; while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); return x; } const int N=5001; struct Edge { int u,v; }; Edge edge[N]; std::vector<std::pair<int,int> > g[N]; bool vis[N],mark[N]; std::stack<std::pair<int,int> > stk; void dfs(const int &x,const int &par) { vis[x]=true; for(unsigned i=0;i<g[x].size();i++) { const int &y=g[x][i].first; if(y==par) continue; stk.push(std::make_pair(x,g[x][i].second)); if(!vis[y]) { dfs(y,x); } else { int z; do { z=stk.top().first; mark[stk.top().second]=true; stk.pop(); } while(z!=y); throw 0; } stk.pop(); } } std::set<int> e[N]; inline void add_edge(const int &u,const int &v) { e[u].insert(v); e[v].insert(u); } inline void del_edge(const int &u,const int &v) { e[u].erase(v); e[v].erase(u); } int s[N],ans[N]; void solve(const int &x,const int &par) { s[++s[0]]=x; for(std::set<int>::iterator i=e[x].begin();i!=e[x].end();i++) { const int &y=*i; if(y==par) continue; solve(y,x); } } inline bool check(int a[],int b[],const int &n) { for(register int i=1;i<=n;i++) { if(a[i]<b[i]) return true; if(a[i]>b[i]) return false; } return false; } int main() { const int n=getint(),m=getint(); for(register int i=0;i<m;i++) { const int &u=edge[i].u=getint(); const int &v=edge[i].v=getint(); g[u].push_back(std::make_pair(v,i)); g[v].push_back(std::make_pair(u,i)); } for(register int i=0;i<m;i++) { add_edge(edge[i].u,edge[i].v); } if(m==n-1) { solve(1,0); for(register int i=1;i<=n;i++) { printf("%d%c",s[i]," \n"[i==n]); } return 0; } try { dfs(1,0); } catch(...) {} std::fill(&ans[1],&ans[n]+1,INT_MAX); for(register int i=0;i<m;i++) { if(!mark[i]) continue; del_edge(edge[i].u,edge[i].v); s[0]=0; solve(1,0); if(check(s,ans,n)) { std::copy(&s[1],&s[n]+1,&ans[1]); } add_edge(edge[i].u,edge[i].v); } for(register int i=1;i<=n;i++) { printf("%d%c",ans[i]," \n"[i==n]); } return 0; }