Dizzy Cows(拓扑)

链接:https://ac.nowcoder.com/acm/contest/1077/D
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
Special Judge, 64bit IO Format: %lld
题目描述
The cows have taken to racing each other around the farm but they get very dizzy when running in circles, and everyone knows that dizzy cows don’t produce any milk. Farmer John wants to convert all of the two-way cow paths in the farm to one-way paths in order to eliminate any ‘cycles’ and prevent the cows from getting dizzy. A ‘cycle’ enables a cow to traverse one or more cow paths and arrive back at her starting point, thus completing a loop or circle.
The farm comprises N pastures (1 <= N <= 100,000) conveniently numbered 1…N. M1 (1 <= M1 <= 100,000) one-way cow paths and M2 two-way cow paths (1 <= M2 <= 100,000) connect the pastures. No path directly connects a pasture to itself, although multiple paths might connect two different pastures. A cow may or may not be able to travel between any two given pastures by following a sequence of cow paths.
Your job is to assign a direction to the two-way cow paths such that the entire farm (ultimately with only one-way paths) has no cycles. That is, there should be no sequence of one-way cow paths which leads back to its starting position. The existing one-way cow paths do not form a cycle and should be left as they are.
One-way cow paths run from pasture Ai (1 <= Ai <= N) to pasture Bi (1 <= Bi <= N). Two-way cow paths connect pastures Xi (1 <= Xi <= N) and Yi (1 <= Yi <= N).
Consider this example:

             1-->2
             |  /|
             | / |
             |/  |
             3<--4

The cow paths between pastures 1 and 3, 2 and 3, and 2 and 4 are two-way paths. One-way paths connect 1 to 2 and also 4 to 3. One valid way to convert the two-way paths into one-way paths in such a way that there are no cycles would be to direct them from 1 to 3, from 2 to 3, and from 3 to 4:

         1-->2
         |  /|
         | / |
         vL  v
         3<--4

输入描述:

  • Line 1: Three space separated integers: N, M1, and M2
  • Lines 2…1+M1: Line i+1 describes a one-way cow path using two space separated integers: Ai and Bi
  • Lines 2+M1…1+M1+M2: Line i+M1+1 describes a two-way cow path using two space separated integers: Xi and Yi
    输出描述:
  • Lines 1…M2: Line i should contain two space-separated integers: either Xi and Yi or Yi and Xi, depending on the direction assigned to the i-th two-way path. The two-way paths must appear in the same order in the output as they do in the input. If there is no solution, output “-1” on a single line.
    示例1
    输入
    复制
4 2 3
1 2
4 3
1 3
4 2
3 2

输出
复制

1 3
4 2
2 3

/*
题意是:给无向边标定方向,使图不构成环。
不构成环,很容易想到拓扑排序

对应代码2:
一开始我是加入了所以边进行拓扑排序(不算无向边的度),
当队列中出来的这个点有无向边与它相连时,输出:当前点–>通过无向边与之相连的点,
但是我不知道如何 标记 那已经输出的那条无向边(即方向已经确定的边)已经确定方向了,
,不标记的化会再输出它的反向边。
(后来傻不拉几的才反映过来实质是一个反向边标记问题
快速找到该条边的反向边标号,并标记不可用)。
如何快速找到一条边的反向边?(每条边已经标号并且方向边标号相邻):

nowID ^1 为反向边的编号,

对应代码1:
不标记反向边的话,要换一种思路:
只需要对有向边上的点做拓扑排序,
记录每个点的出场顺序(拓扑序),
让拓扑序小的指向大的就ok。

*/
代码一:

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+5;
struct Edge
{
    int to;
    int val;
    int nxt;
} edge[N<<1];
int head[N],idx,top[N];
int in[N];
int n,m1,m2;
void add_edge(int from,int to,int val)
{
    edge[idx].to = to;
    edge[idx].val = val;
    edge[idx].nxt = head[from];
    head[from] = idx++;
    in[to]++;
}

void topSort()
{
    queue<int>q;
    int tp = 0;
    for(int i = 1; i <= n ; i++)
    {
        if(!in[i])
        {
            q.push(i);
            top[i] = ++tp;
        }
    }
    while(!q.empty())
    {
        int k = q.front();
        q.pop();
        for(int i = head[k]; ~i; i = edge[i].nxt)
        {
            int t = edge[i].to;
            if(!(--in[t]))
            {
                q.push(t);
                top[t] = ++tp;
            }
        }
    }
}
int main()
{
    cin>>n>>m1>>m2;
    int x,y;
    memset(head,-1,sizeof(head));
    for(int i = 0; i < m1; i++)
    {
        cin>>x>>y;
        add_edge(x,y,1);
    }
    topSort();
    for(int i = 0; i < m2; i++)
    {
        cin>>x>>y;
        if(top[x] < top[y])
            cout<<x<<" "<<y<<endl;
        else
            cout<<y<<" "<<x<<endl;
    }
    return 0;
}

代码二:

//标记 无向边中已经确定方向的 反向边

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+5;
int n,m1,m2,idx;
struct Edge
{
    int from;
    int to;
    int val;
    int nxt;
} edge[N<<1];
int head[N],in[N];
//head[i]存i扩展出来的边中最大的那个编号(即最后一条扩展边的编号)
inline void add_edge(int from,int to,int val)
{
    edge[idx].from = from;
    edge[idx].to = to;
    edge[idx].val = val;
    edge[idx].nxt = head[from];
    head[from] = idx++;
}
void topSort()
{
    queue<int>q;
    for(int i = 1; i <= n; i++)
    {
        if(!(in[i])) q.push(i);
    }
    while(!q.empty())
    {
        int k = q.front();
        q.pop();
        for(int i = head[k]; ~i; i = edge[i].nxt)
        {
            if(edge[i].val == 1)
            {
                int t = edge[i].to;
                if(!(--in[t])) q.push(t);
            }
            else if (edge[i].val == 2)
            {
                //cout<<edge[i].from<<" "<<edge[i].to<<endl;
                edge[i^1].val = -1;
            }
        }
    }
}
int main()
{
    memset(head,-1,sizeof(head));
    cin>>n>>m1>>m2;
    int x,y;
    for(int i = 0; i < m1; i++)
    {
        cin>>x>>y;
        add_edge(x,y,1);
        in[y]++;
    }
    /*
    边标号从0开始,
    如果前面已经标号边数为奇数,
    下一个idx也是奇数!!
    为了后面找后面边的反边,
    此时前面标号边数一定要构成一个偶数,
    即让下一条边标号为偶数。
    前面边数不够,只能虚拟一条边。
    */
    if(idx&1) idx++;
    for(int i = 0; i < m2; i++)
    {
        cin>>x>>y;
        add_edge(x,y,2);
        add_edge(y,x,2);
    }
    topSort();
    for(int i = 0; i < idx; i++)
    {
        if(edge[i].val == 2)
            cout<<edge[i].from<<" "<<edge[i].to<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Leo Bliss

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值