codeforces 589H DFS

本文深入探讨了一道涉及图论的难题,即在给定连通图和特殊点的情况下,如何最大化路径数量。通过分析图转换为树的过程、匹配原则以及DFS搜索算法的应用,详细阐述了解决策略。同时,介绍了使用迭代器简化容器操作的技巧,并提供了参考代码以辅助理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【题目链接】
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=106786#problem/D

【解题报告】
给定n个点和m条边的无向连通图,和k个特殊点,任意两个特殊点之间连一条路,任意一个特殊点不能是两条路的端点。问最多能连多少条路。
这个题目我是不会的(图论思维真的很弱。。。)。
这个题还是重点要学习怎么分析题目的。

对于一个连通块,我们通过深搜把它变成树,如果它内部有2i个匹配点,则两两匹配。如果有2i+1个匹配点,那么就出现一个不能匹配的点,它需要上溯和他的父节点或者兄弟子树中的节点来匹配。依据此来进行DFS搜索。
一个连通块里如果有k个特殊点,那么一定存在k/2条路。注意题目没有说明给出的图是联通图,所以数据可能是多个联通块,处理的时候还需要维护一下路径总数。

寻找路径的方法是DFS。因为没有写过这类题,不太清楚这种把图当作树处理的代码的实现细节。所以学习了一下别人的代码。
http://blog.youkuaiyun.com/playwfun/article/details/49618017

这里面对容器使用了泛型迭代器.for+auto指遍历这个容器的所有元素。需要学习使用这种迭代器(比iterator简洁)

【参考代码】

#include<bits/stdc++.h>
using namespace std;

const int maxn=5e5+100;

int pa[maxn],dep[maxn],link[maxn];
bool mark[maxn],vis[maxn];
vector<int>G[maxn];
int n,m,k;

int ans=0;
int dfs( int u, int fa )
{
      vis[u]=1; pa[u]=fa;
      dep[u]=(fa==-1 ? 0 : dep[fa] )+1;
      int now=mark[u]?u:-1;
      for( auto v:G[u] )
      {
            if(vis[v])continue;
            int It=dfs( v,u ); //返回一个子树内一个没有匹配的特殊点
            if(It==-1)continue;
            if(now==-1)now=It; //以特殊点为起点
            else{
                  link[now]=It;
                  link[It]=now;
                  now=-1;
                  ans++;
            }
        }
        return now;
}

void print()
{
      memset( vis,0,sizeof vis );
      for( int i=1;i<=n;i++ )if( link[i] && !vis[i] )
      {
            vis[i]=vis[link[i]]=1;
            int u=i,v=link[i];
            vector<int>lt,rt;
            lt.push_back(u);
            rt.push_back(v);
            int cnt_=0;
            while( u!=v ){
                  if(dep[u]<dep[v])rt.push_back(pa[v]),v=pa[v];
                  else lt.push_back(pa[u]),u=pa[u];
            }
            for( int i=rt.size()-2;i>=0;i-- )lt.push_back( rt[i] );
            cout<<lt.size()-1;
            for( int i=0;i<lt.size();i++ )
                  cout<<" "<<lt[i];
            cout<<endl;
      }
}

int main()
{
      cin>>n>>m>>k;
      int x,y;
      for( int i=1;i<=m;i++ )
      {
            int x,y;   cin>>x>>y;
             G[x].push_back(y);
             G[y].push_back(x);
      }
      for( int i=1;i<=k;i++ )
      {
            int x; cin>>x;
            mark[x]=true;
      }

      for( int i=1;i<=n;i++ )if(!vis[i])dfs(i,-1);
      cout<<ans<<endl;
      print();

      return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值