hdu5215 求无向图中是否有奇环和偶环 :lca+dfs使图->树

本文介绍了一种通过深度优先搜索(DFS)检测图中是否存在奇数或偶数长度环的方法,并提供了详细的算法实现步骤。该算法首先进行DFS扫描以找到可能的环,接着通过最近公共祖先(LCA)计算确定环的长度是奇数还是偶数。

队友给的思路,感觉思路很好啊==

对图进行dfs扫描,假设u->v,遇到已经扫过的点v,计算当前点u和v的lca,lca和u以及v之间形成了一个环(计算奇偶)

这样只能计算有没有简单的奇偶环,比如两个简单的奇环在一起就成了偶环(无论两个奇环共享几条边)

所以在dfs中扫完子树之后,看有没有找到过两个奇环,如果奇环>=4就有了一个偶环(双向边,等价于两个奇环)

具体实现可以见代码==

  1 #pragma comment(linker, "/STACK:102400000,102400000")
  2 #include<stdio.h>
  3 #include<string.h>
  4 #include<algorithm>
  5 #define maxn 100005
  6 #define maxm 600005
  7 using namespace std;
  8 int Now,Next[maxm],Head[maxm],Point[maxm];
  9 int parent[maxn][25],depth[maxn];
 10 int ans1,ans2,vis[maxn];
 11 void Add(int u,int v)
 12 {
 13   Next[++Now]=Head[u];
 14   Head[u]=Now;
 15   Point[Now]=v;
 16 }
 17 void dfs1(int u,int pre,int deep)
 18 {
 19   vis[u]=1;
 20   parent[u][0]=pre;
 21   depth[u]=deep;
 22   for (int i=Head[u];i;i=Next[i])
 23   {
 24     int v=Point[i];
 25     if (v==pre||vis[v]) continue;
 26     dfs1(v,u,deep+1);
 27   }
 28 }
 29 void double_build(int n)
 30 {
 31   int i,j;
 32   for (i=0;i<20;i++)
 33     for (j=1;j<=n;j++)
 34       if (parent[j][i]<=0) parent[j][i+1]=0;
 35       else parent[j][i+1]=parent[parent[j][i]][i];
 36 }
 37 int lca(int u,int v)
 38 {
 39   int i;
 40   if (depth[u]>depth[v]) swap(u,v);
 41   for (i=0;i<=20;i++)
 42     if ((depth[v]-depth[u])>>i&1)
 43       v=parent[v][i];
 44   if (u==v) return u;
 45   for (i=20;i>=0;i--)
 46     if (parent[u][i]!=parent[v][i])
 47     {
 48       u=parent[u][i];
 49       v=parent[v][i];
 50     }
 51   return parent[u][0];
 52 }
 53 void solve(int u,int v)
 54 {
 55   int fa=lca(u,v);
 56   int tmp=depth[u]+depth[v]-2*depth[fa]+1;
 57   if (tmp%2) ans1++;
 58   else ans2++;
 59 }
 60 void dfs2(int u,int pre)
 61 {
 62   int i,v;
 63   vis[u]=1;
 64   for (i=Head[u];i;i=Next[i])
 65   {
 66     v=Point[i];
 67     if (v==pre) continue;
 68     if (vis[v]) solve(u,v);
 69     else dfs2(v,u);
 70   }
 71   if (ans1>=4) ans2++; //thinking
 72 }
 73 int main()
 74 {
 75   int T,n,m,i,u,v,odd,even;
 76   scanf("%d",&T);
 77   while (T--)
 78   {
 79     scanf("%d%d",&n,&m);
 80     Now=0;
 81     memset(Head,0,sizeof(Head));
 82     for (i=1;i<=m;i++)
 83     {
 84       scanf("%d%d",&u,&v);
 85       Add(u,v); Add(v,u);
 86     }
 87     memset(parent,0,sizeof(parent));
 88     memset(vis,0,sizeof(vis));
 89     for (i=1;i<=n;i++)
 90       if (!vis[i]) dfs1(i,0,1);
 91     double_build(n);
 92     memset(vis,0,sizeof(vis));
 93     odd=even=0;
 94     for (i=1;i<=n;i++)
 95       if (!vis[i])
 96       {
 97         ans1=ans2=0;
 98         dfs2(i,0);
 99         odd=max(odd,ans1);
100         even=max(even,ans2);
101         if (odd&&even) break;
102       }
103     if (odd) printf("YES\n");
104     else printf("NO\n");
105     if (even) printf("YES\n");
106     else printf("NO\n");
107   }
108   return 0;
109 }
View Code

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5215

 

--------------------------华丽分割---------------------

蛤蛤这方法是错的==

我就是来卖个萌明天两门祝不全挂

转载于:https://www.cnblogs.com/xiao-xin/articles/4474201.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值