cf(tarjan运用+链表)

/*给你有向图,有些边是可以直接走的,有些边是修之后才可以走,现在要想从1能到达所有的点,问你至少要修多少条路。
解:现把 可以直接 走的边加进来,缩点,因为(1<n,m<1e6)。
      然后从1点bfs,每个点初始都为0,可以直接的标记为-1,要修的边标记为边的编号,当再有不修路就能到达的话又把它标记为-1,。
      所以,如果路全部修之后好不能到达,则有些点还为0。
      点的标记>0,则说明修路之后才能到达。 
      要记录边的编号,应该用数组模拟邻接链表。
*/
#include<cstdio>
#include<cstring>
#include<iostream>
#define N 100030
using namespace std;

int head[N];
int n,m,x,y;
int dfn[N],low[N],instack[N],num[N],edge[N],f[N],o[N],top,D,now,ans;
bool inst[N];
struct Node
{
    int u,v,f;
    int next;
}ed[N];
void tarjan(int i)
{
    dfn[i]=low[i]=++D;
    inst[i]=1,instack[++top]=i;
    for (int p=head[i]; p; p=ed[p].next)
    {
        int j=ed[p].v;
        if(ed[p].f)continue;
        if (!dfn[j]) tarjan(j),low[i]=min(low[i],low[j]);
        else if (inst[j]) low[i]=min(low[i],dfn[j]);
    }
    if (low[i]==dfn[i])
    {
        now++;
        int k;
        do k=instack[top--],inst[k]=0,num[k]=now;
        while (k!=i);
    }
}

void bfs(int x)
{
    int h=0,t=1;
    f[1]=x;
    edge[x]=-1;
    while (h<t)
    {
        int i=f[++h];
        for (int p=head[i]; p; p=ed[p].next)
        {
            //int j=vv[p];
            int j=ed[p].v;
            if (!edge[j]) edge[j]=ed[p].f ?p:-1,f[++t]=j;
            else if (edge[j]>0 && !ed[p].f) edge[j]=-1;
        }
    }
}

void init()
{
    memset(head,0,sizeof(head));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(edge,0,sizeof(edge));
    top=0;
    D=0;
    now=0;

}
void add(int u,int v,int f,int e)
{
    ed[e].u=u;
    ed[e].v=v;
    ed[e].f=f;
    ed[e].next=head[u];
    head[u]=e;
}
int main()
{
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        init();
        int u,v,f;
        for (int i=1; i<=m; i++)
        {
            cin>>u>>v>>f;
            add(u,v,f,i);
        }

        for (int i=1; i<=n; i++)
            if (!dfn[i])
                tarjan(i);

        memset(head,0,sizeof(head));
        for (int i=1; i<=m; i++)
        {
            //int x=num[uu[i]],y=num[vv[i]];
            //vv[i]=y;
           // if (x!=y) next[i]=head[x],head[x]=i;
           int x=num[ed[i].u];
           int y=num[ed[i].v];
           int f=ed[i].f;
           if(x!=y)
           {
               add(x,y,f,i);
           }
        }

        bfs(num[1]);
        ans=0;
        for (int i=1; i<=now; i++)
            if (edge[i]>0)
                o[++ans]=edge[i];
        for (int i=1; i<=now; i++)
            if (!edge[i])
                ans=-1;
        printf("%d\n",ans);
        for (int i=1; i<=ans; i++)
            printf("%d%s",o[i],i<ans?" ":"\n");
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值