1077. Travelling Tours @ Timus Online Judge
大致题意就是一个无向图,让你找 $T$ 个环(环上点不能重复),要求每个环都至少含有一条边是其余边没有的,并且 $T$ 最大,然后让你输出这 $T$ 个环,有SPJ。
思路十分简单:
提示1:这道题目存在一个结论。
提示2:结论:一个 $n$ 个点 $m$ 条边的无向连通图一定有 $m-n+1$ 个环。
提示3:这道题的算法是 $DFS$ 加贪心算法。
提示4:考虑树加一条边变成一个环。
先证明一下结论:
考虑一个图的生成树,在树上加一条边就会变出一个环,这样就可以形成 $m-n+1$ 个环,但是这是不是上限呢?证明一下:
考虑已经选了 $T$ 个环,那么怎么选择 $T+1$ 个环呢,将环上不同于其他环的边称为特殊边,选新环即是在删掉了特殊边的原图上找环。在这之前证明一个性质:删掉特殊边不影响图的连通性。
先证明不破坏连通性的性质:如果删除了特殊边 $x,y$ ,假设 $tx,ty$ 连通性被破坏,因为环所以 $x,y$ 之间存在两条路径,则原来存在一条不经过 $x-y$ 的 $tx-x-y-ty$ 的路径,所以删除后连通性并没有被破坏,矛盾,证毕。
所以,我们假设现在有 $T$ 个环( $T>m-n+1$ ),从中挑出 $m-n+1$ 个,删除掉其的特殊边,则剩余$n-1$ 条边,则图变成了一棵树,则不可能存在一个环,与 $T>m-n+1$ 矛盾,因此结论成立。
这样,这道题目就简单许多了,我们只需要暴力DFS就行了。
时间复杂度:$O(n+m+$环上点数的总和$)$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| #include<cstdio> #include<cstring> #define N 210 #define M 41000 using namespace std; struct node { int y,next; }a[M];int len=1,last[N]; inline void ins(int x,int y){len++;a[len].y=y;a[len].next=last[x];last[x]=len;} int n,m; int fa[N]; int ans[1100000],ll[M],rr[M],cnt,top,dep[N]; inline void findans(int x,int y) { if(dep[x]<dep[y])return ; cnt++;ll[cnt]=top+1; while(x!=y) { ans[++top]=x; x=fa[x]; } ans[++top]=y; rr[cnt]=top; } void dfs(int x) { for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(y!=fa[x]) { if(!dep[y]) { fa[y]=x;dep[y]=dep[x]+1; dfs(y); } else { findans(x,y); } } } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int x,y;scanf("%d%d",&x,&y); ins(x,y);ins(y,x); } for(int i=1;i<=n;i++) { if(!dep[i]) { dep[i]=1; dfs(i); } } printf("%d\n",cnt); for(int i=1;i<=cnt;i++) { printf("%d ",rr[i]-ll[i]+1); for(int j=rr[i];j>=ll[i];j--)printf("%d ",ans[j]); printf("\n"); } return 0; }
|
吐槽:
语文不好,请见谅。
你以为我很会这道题,TM我是看题解的。
TMD没做出来,怎么当时就一点都没有想到DFS树呢?为什么当时想的结论一个比一个奇葩呢???
。。。