3 条题解
-
0
首先考虑前 60 分,即一棵树的做法。
由于要求字典序最小,我们很容易想到的就是贪心的做法,即从 1 号节点开始,每次走编号最小的一条,易证这样的字典序是最小的,那么代码实现时我们如果还用链式前向星的话,写起来就会非常的麻烦。
于是考虑 vector 存图,对于每个点把所有和它连结的点的编号从小到大排序。
因为我们在使用 vector 时,代码是这么写的:for(int i=0;i<v[u].size();i++)所以可以保证每次往最小的一边走,然后我们从1号节点出发,跑一遍 Dfs 就可以得到答案了。
代码如下(15/25 60分做法):#include<bits/stdc++.h> using namespace std; int n,m,ans[5005],tot,a[5005],lat[5005],sum; vector <int> v[5005]; bool Check() { for(int i=1;i<=n;i++) if(!a[i]) return 0; for(int i=1;i<=n;i++) if(a[i]!=ans[i]) return ans[i]>a[i]; } void Dfs(int u,int faz) { if(sum>n) return; a[sum]=u; for(int i=0;i<v[u].size();i++) { if(v[u][i]==faz) continue; sum++; Dfs(v[u][i],u); } } int main() { memset(ans,60,sizeof(ans)); scanf("%d %d",&n,&m); for(int i=1;i<=m;i++) { int x,y; scanf("%d %d",&x,&y); v[x].push_back(y); v[y].push_back(x); } for(int i=1;i<=n;i++) sort(v[i].begin(),v[i].end()); sum=1; Dfs(1,0); memcpy(ans,a,sizeof(a)); for(int i=1;i<=n;i++) printf("%d ",ans[i]); return 0; }那还有剩下的 40 分呢?其实也不难,我们可以枚举删除一条边,再按原来的办法跑 Dfs 就行。
但为什么这样可行呢?因为如果一个图里有环的话( 在没有重边自环的情况下是肯定有环的),这一个环里肯定有一条边是用不到的,正确性显而易见,因为我们下一步只能往没走过的地方走,或者是朝上一个地方走,在这种情况下我们能走的点是 ,而能经过的边只有 条,对于一个 个点 个边的环来说,肯定是有一条边用不到的(基环树嘛),所以我们就枚举这条用不到的边,然后跑 Dfs 即可。
但事实证明如果直接枚举所有边在洛谷数据会超时,T 了最后两个点,开O2是可以过,但还有一种我脑补的优化方法,就是先跑一遍 Dfs,找到环,然后只删环上的边,为什么呢?因为我们可以想到,如果你删的边不是在环上而在别的地方,如果1号节点还能走到环上,时间复杂度是会跑满的(这也是为什么我在程序里用了一个cir的布尔变量),这样就很容易超时了。
这种写法就交给各位了,这里附上我的代码:
(23/25 92 做法,O225/25 100)// luogu-judger-enable-o2 #include<bits/stdc++.h> using namespace std; int n,m,ans[5005],tot,a[5005],lat[5005],sum,fr[5005],to[5005],del; vector <int> v[5005]; bool cir; bool Check() { for(int i=1;i<=n;i++) if(!a[i]) return 0; for(int i=1;i<=n;i++) if(a[i]!=ans[i]) return ans[i]>a[i]; } void Dfs(int u,int faz) { if(sum>n) { cir=1; return; } a[sum]=u; for(int i=0;i<v[u].size();i++) { if((u==fr[del] && v[u][i]==to[del]) || (v[u][i]==fr[del] && u==to[del]) || v[u][i]==faz) continue; sum++; Dfs(v[u][i],u); } } int main() { memset(ans,60,sizeof(ans)); scanf("%d %d",&n,&m); for(int i=1;i<=m;i++) { int x,y; scanf("%d %d",&x,&y); v[x].push_back(y); v[y].push_back(x); fr[i]=x; to[i]=y; } for(int i=1;i<=n;i++) sort(v[i].begin(),v[i].end()); if(m==n-1) { sum=1; Dfs(1,0); memcpy(ans,a,sizeof(a)); } else if(n==m) { for(int i=1;i<=m;i++) { memset(a,0,sizeof(a)); del=i; sum=1; cir=0; Dfs(1,0); if(!cir && Check()) memcpy(ans,a,sizeof(a)); } } for(int i=1;i<=n;i++) printf("%d ",ans[i]); return 0; } -
0
D30 基环树 P5022 [NOIP2018 提高组] 旅行_哔哩哔哩_bilibili
P5022 [NOIP 2018 提高组] 旅行 - 洛谷
基环树的遍历最小字典序问题:出边排序。暴力断边。剪枝优化。
// 基环树 遍历最小字典序 暴力 O(n^2) #include<bits/stdc++.h> using namespace std; const int N=5010; int n,m,a,b; vector<int> e[N]; pair<int,int> edge[N]; int du,dv,vis[N],cnt,better; vector<int> path(N,N); void dfs1(int x){ //树搜索 vis[x]=1; path[cnt++]=x; for(int y:e[x])if(!vis[y]) dfs1(y); } bool dfs2(int x){ if(!better){ //剪枝:若序号变大则回退,变小则走完 if(x>path[cnt]) return 1; if(x<path[cnt]) better=1; } vis[x]=1; path[cnt++]=x; for(int y:e[x]){ if(vis[y])continue; if(y==du&&x==dv)continue; if(y==dv&&x==du)continue; if(dfs2(y)) return 1; } return 0; } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ scanf("%d%d",&a,&b); e[a].push_back(b); e[b].push_back(a); edge[i]={a,b}; } for(int i=1;i<=n;i++)sort(e[i].begin(),e[i].end()); //出边排序 if(m==n-1) dfs1(1); else{ for(int i=1;i<=m;i++){ //暴力断边 du=edge[i].first; dv=edge[i].second; memset(vis,0,sizeof vis); cnt=better=0; dfs2(1); } } for(int i=0;i<n;i++)printf("%d ",path[i]); } -
0
#include<bits/stdc++.h> using namespace std; const int N=5010; vector<int>G[N]; pair<int, int> edge[N]; int better, cnt, dx, dy, v[N], path[N]; bool dfs(int x) { if(!better && x>path[cnt+1])return 0; if(!better && x<path[cnt+1])better=1; v[x]=1; path[++cnt]=x; for(int y:G[x]) if(!v[y]) { if((x==dx&&y==dy)||(y==dx&&x==dy))continue; if(!dfs(y))return 0; } return 1; } int main() { int n, m;scanf("%d%d", &n, &m); for(int i=1, x, y; i<=m; i++) { scanf("%d%d", &x, &y); G[x].push_back(y); G[y].push_back(x); edge[i]= {x, y}; } for(int i=1; i<=n; i++)sort(G[i].begin(), G[i].end()); memset(path, 0x3f, sizeof(path)); if(n==m+1) dfs(1); else { for(int i=1; i<=m; i++) //枚举断边 { dx=edge[i].first; dy=edge[i].second; memset(v, 0, sizeof(v)); cnt=better=0; bool bk=dfs(1); } } for(int i=1; i<=n; i++)printf("%d ", path[i]); return 0; }
- 1
信息
- ID
- 809
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 8
- 标签
- 递交数
- 91
- 已通过
- 17
- 上传者