2017 Multi-University Training Contest - Team 9:FFF at Valentine

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6165

题目意思:FFF的牢房是一个n个节点m条边的有向图,图无自环,一对情侣被FFF关了起来

(二者关在哪里不知道,且不关在同一个点)。假如一个在点u一个在点v,能从点u到点v或

者能从点v到点u,则情侣就可以获救。即情侣双方任意一方能到达伴侣关押的位置,他们两个

就能获救。所以我们现在就要判断一个图,是否对于图中任意的点u-v都要存在u->v或v->u.



官方题解:缩点为DAG,则如果在拓扑序中出现了有两个及以上入度为0的点则不合法

昨天比赛的时候,我先用强连通缩点,然后跑DFS,然后WA掉了,今天再看,发现自己

1忘记判图的连通性,2就是dfs搜索的方法不太对。昨天又改了缩完点后,判断新图存不存

再欧拉通路,这个带判断图的连通性了,结果还WA,奔溃。现在没想出哪有错,感觉思路

也没错,假如图连通,缩点后图还连通,选择度为0的节点,开始然后把图中点串成一长串。

PS:队友写最短路解法,我写强连通缩点,最后都没过,真心弱啊。


解题思路:

首先对图进行缩点,对于一个图,对于一个连通分量来说,其内部所有的点都是可以相互到

达的,所以先用强连通缩点去环,并将一个环内的点进行统一的编号(即缩点)。对于一个

有向图,如果连通分量个数是1,那么这个图就是强连通的,则一定能够获救。否则我们构建

新的图,并统计节点的入度,首先入度为0的点只能有1个,超过一个的话,说明图本身不连通,

并且比较容易想到这个唯一的入度为0的点是我们dfs的起点,从这个点如果能把新图中的点

串成一个线性的长串,则情侣能够获救。所以我们要求dfs的最大深度,只有最大深度等于新

图中的节点个数的时候,才能说明从起点把图中的点串成了一长串,否则串不成一长串,情侣

也不能获救。


#include <iostream>
#include <stdio.h>
#include <string.h>

using namespace std;

const int maxn = 2000;
const int maxm = 10000;

struct Edge
{
    int u,v,nex;
}edge[maxm];
int head[maxm];   ///链表头节点
bool inStack[maxn];   ///标记是否在栈中
int Stack[maxn];      ///自写栈
int belong[maxn];     ///各个顶点属于那些连通分量
int dfn[maxn];        ///时间戳
int low[maxn];        ///u或u的子树能够追溯到的最早的栈中节点的序号
int in[maxn];         ///统计节点入度
int vis[maxn];
int cnt_edge,time,top,cnt,n,m,deepest;
///cnt_edge统计边数,time时间戳,top栈顶指针,cnt连通分量的个数,n节点数,m边数,deepest深搜最大深度
///加边函数
void addEdge(int u,int v)
{
    edge[cnt_edge].u = u;
    edge[cnt_edge].v = v;
    edge[cnt_edge].nex = head[u];
    head[u] = cnt_edge++;
}
///强连通缩点。
void tarjan(int u)
{
    dfn[u] = low[u] = ++time;
    Stack[++top] = u;
    inStack[u] = true;
    for(int i = head[u]; i != -1; i = edge[i].nex)
    {
        int v = edge[i].v;
        if(!dfn[v])  ///没有被访问过
        {
            tarjan(v);
            low[u] = min(low[u],low[v]);
        }
        else if(inStack[v])  ///在栈中
            low[u] = min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u])
    {
        cnt++;    
        int temp;
        do
        {
            temp = Stack[top--];
            inStack[temp] = false;
            belong[temp] = cnt;   ///节点temp属于第cnt个连通分量
        }while(temp != u);
    }
}
void solve()
{
    top = time = cnt = 0;
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    for(int i = 1; i <= n; i++)
    {
        if(dfn[i]==0)
            tarjan(i);
    }
}
void dfs(int x,int depth)
{
    vis[x] = 1;
    deepest = max(depth,deepest);
    for(int i = head[x]; i != -1; i = edge[i].nex)
    {
        int v = edge[i].v;
        if(vis[v]==0)
        {
            dfs(v,depth+1);
            vis[v] = 0;
        }
    }
}
int main()
{
    int t,u,v;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        memset(head,-1,sizeof(head));
        cnt_edge = 0;
        for(int i = 1; i <= m; i++)
        {
            scanf("%d%d",&u,&v);
            addEdge(u,v);
        }
        solve();         ///强连通缩点
        if(cnt == 1)     ///整个图只有一个连通分量,则图强连通
        {
            printf("I love you my love and our love save us!\n");
        }
        else
        {
            ///重建图
            memset(in,0,sizeof(in));
            memset(head,-1,sizeof(head));
            int num = cnt_edge;
            cnt_edge = 0;
            for(int i = 0; i < num; i++)
            {
                u = edge[i].u;
                v = edge[i].v;
                int newu = belong[u];
                int newv = belong[v];
                if(newu != newv) ///两个点不在一个集合,即不在同一个环中,则建立关系
                {
                    in[newv]++;
                    addEdge(newu,newv);
                }
            }
            int start = 1,ccount = 0; 
            ///ccount用来统计入度为0的点的个数
            for(int i = 1; i <= cnt; i++)
            {
                if(in[i]==0)
                {
                    start = i;
                    ccount++;
                }
            }
            ///入度为0的点最多1个,两个和两个以上说明图本身不连通。
            if(ccount==1)
            {
                memset(vis,0,sizeof(vis));
                deepest=0;
                dfs(start,1);
            }
            if(ccount==1 && deepest==cnt)
                printf("I love you my love and our love save us!\n");
            else
                printf("Light my fire!\n");
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值