poj 1515 Street Directions(双连通分量)

题意:给出一个连通图,边是双向边,要求令尽可能多的双向边改成单向边,并且图还是连通的(强连通)。

思路:这题其实不太难搞。我们可以想一下,那些边是一定不能改造的?没错,是桥,如果桥被改成单向边,那么就不可能强连通,另外,对于一个双连通分量来说,一定可以把它们的边改成单向并且还是连通的。这样,算法就比较明确了,对于桥来说,不能改造,对于一个双连通分量来说,改造它的所有边。如何改造双连通分量中的边?其实仔细想一下就可以发现,tarjan的过程中的遍历顺序恰好是一个可行的方案,因此,可以再tarjan的过程中直接把结果输出,用一个数组标记一条边是否被改造即可。


代码:


#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=1000+10;
const int maxm=10000+10;
struct Edge
{
    int v,next;
    Edge(){};
    Edge(int vv,int nx){v=vv;next=nx;}
}edges[maxm<<1];
int head[maxn],nEdge;
int pre[maxn],id[maxn],ebc_cnt,dfs_clock;
bool flag[maxm<<1];
stack<int>S;
void AddEdge(int u,int v)
{
    edges[++nEdge]=Edge(v,head[u]);
    head[u]=nEdge;
    edges[++nEdge]=Edge(u,head[v]);
    head[v]=nEdge;
}
int tarjan(int u,int fa)
{
    int lowu=pre[u]=++dfs_clock;
    S.push(u);
    for(int k=head[u];k!=-1;k=edges[k].next)
    {
        int v=edges[k].v;
        if(!pre[v])
        {
            int lowv=tarjan(v,u);
            lowu=min(lowu,lowv);
            if(lowv>pre[u])
            {
                printf("%d %d\n",u,v);
                printf("%d %d\n",v,u);
                flag[k]=flag[k^1]=true;
                ebc_cnt++;
                while(true)
                {
                    int x=S.top();S.pop();
                    id[x]=ebc_cnt;
                    if(x==v) break;
                }
            }
        }
        else if(pre[v]<lowu&&v!=fa)
            lowu=min(lowu,pre[v]);
        if(!flag[k]&&v!=fa)
        {
            flag[k]=flag[k^1]=true;
            printf("%d %d\n",u,v);
        }
    }
    return lowu;
}
void find_ebc(int n)
{
    memset(pre,0,sizeof(pre));
    memset(flag,0,sizeof(flag));
    memset(id,0,sizeof(id));
    while(!S.empty()) S.pop();
    dfs_clock=ebc_cnt=0;
    for(int i=1;i<=n;++i)
        if(!pre[i]) tarjan(i,-1);
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n,m;
    int tcase=0;
    while(~scanf("%d%d",&n,&m))
    {
        if(n==0&&m==0) break;
        printf("%d\n\n",++tcase);
        int u,v;
        memset(head,0xff,sizeof(head));
        nEdge=-1;
        for(int i=0;i<m;++i)
        {
            scanf("%d%d",&u,&v);
            AddEdge(u,v);
        }
        find_ebc(n);
        printf("#\n");
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值