codeforces 589H DFS

【题目链接】
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.csdn.net/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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值