hdu2767&&hdu3836 Proving Equivalences(Tarjan+缩点)

来源:互联网 发布:软件板块股票行情 编辑:程序博客网 时间:2024/06/06 05:24


http://acm.hdu.edu.cn/showproblem.php?pid=2767

题意:给你n个命题,m个推导,求增加多少条推导就能使命题两两等价。


ps:这里终于看懂九野巨的模板了,刚开始就是边的输入方式不懂,怎么模拟都不对。后来一想,这尼玛是头插法啊,我就说怎么似曾相识。看来我数据结构水的一比啊。也好,让自己复习下数据结构。


思路:命题转化为节点,推导转化为边,求再增加多少条边就可以将其变为强连通图。不同于上一道题判断是否为连通图,这题求的是添加的边数。这里采用了把强连通分量转化为缩点,这样就变成了有向无环图。连接有向无环图中入度为0与出度为0的点,相当于入度为0的有root个点,出度为0的有leaf个点,统计数量。因为肯定有剩余的,而剩余的乱连就行,而乱连也不会影响其连通性,所以要求最大值。关于缩点详解,看的这篇博客。主要就是将其染色,不同颜色的累加到需要连接的数组中,便于统计。


#include <stdio.h>#include <algorithm>#include <stdlib.h>#include <string.h>#include <iostream>#include <stack>#include <vector>using namespace std;typedef long long LL;const int N = 100010;const int INF = 1e8;stack<int>S;int dfn[N], low[N], head[N], Belong[N], in[N], out[N], countt, time, n, m, pos;bool instack[N];struct Edge{    int to, next;}edge[N];void add(int u, int v){    edge[pos].to = v;    edge[pos].next = head[u];    head[u] = pos++;}void init(){    time = countt = pos = 0;    memset(in, 0, sizeof(in));    memset(out, 0, sizeof(out));    memset(dfn, 0, sizeof(dfn));    memset(low, 0, sizeof(low));    memset(head, -1, sizeof(head));    memset(instack, false, sizeof(instack));}void Tarjan(int u){    int v;    dfn[u] = low[u] = ++time;    instack[u] = true;    S.push(u);    for(int i = head[u]; i != -1; i = edge[i].next)    {        v = edge[i].to;        if(dfn[v] == 0)        {            Tarjan(v);            low[u] = min(low[u], low[v]);        }        else if(instack[v] == 1)        {            low[u] = min(low[u], dfn[v]);        }    }    if(dfn[u] == low[u])    {        countt ++;        do        {            v = S.top();            S.pop();            instack[v] = false;            Belong[v] = countt;        }while(u != v);    }}int main(){   // freopen("in.txt", "r", stdin);    int t, u, v;    scanf("%d", &t);    while(t--)    {        scanf("%d%d", &n, &m);        init();        for(int i = 1; i <= m; i++)        {            scanf("%d%d", &u, &v);            add(u, v);        }        //缩点        for(int i = 1; i <= n; i++)        {            if(dfn[i] == 0)                Tarjan(i);        }        if(countt == 1)        {            printf("0\n");            continue;        }        for(int i = 1; i <= n; i++)        {            for(int k = head[i]; k != -1; k = edge[k].next)            {                int j = edge[k].to;                if(Belong[i] != Belong[j])                {                    out[Belong[i]]++;                    in[Belong[j]]++;                }            }        }        //找叶子和根度为0的数量        int root = 0, leaf = 0;        for(int i = 1; i <= countt; i++)        {            if(in[i] == 0) root++;            if(out[i] == 0) leaf++;        }        printf("%d\n", max(root, leaf));    }    return 0;}

顺便贴上hdu3836,代码几乎一毛一样。


#include <stdio.h>#include <algorithm>#include <stdlib.h>#include <string.h>#include <iostream>#include <stack>#include <vector>using namespace std;typedef long long LL;const int N = 100010;const int INF = 1e8;stack<int>S;int dfn[N], low[N], head[N], Belong[N], in[N], out[N], countt, time, n, m, pos;bool instack[N];struct Edge{    int to, next;}edge[N];void add(int u, int v){    edge[pos].to = v;    edge[pos].next = head[u];    head[u] = pos++;}void init(){    time = countt = pos = 0;    memset(in, 0, sizeof(in));    memset(out, 0, sizeof(out));    memset(dfn, 0, sizeof(dfn));    memset(low, 0, sizeof(low));    memset(head, -1, sizeof(head));    memset(instack, false, sizeof(instack));}void Tarjan(int u){    int v;    dfn[u] = low[u] = ++time;    instack[u] = true;    S.push(u);    for(int i = head[u]; i != -1; i = edge[i].next)    {        v = edge[i].to;        if(dfn[v] == 0)        {            Tarjan(v);            low[u] = min(low[u], low[v]);        }        else if(instack[v] == 1)        {            low[u] = min(low[u], dfn[v]);        }    }    if(dfn[u] == low[u])    {        countt ++;        do        {            v = S.top();            S.pop();            instack[v] = false;            Belong[v] = countt;        }while(u != v);    }}int main(){   // freopen("in.txt", "r", stdin);    int u, v;    while(~scanf("%d%d", &n, &m))    {        init();        for(int i = 1; i <= m; i++)        {            scanf("%d%d", &u, &v);            add(u, v);        }        //缩点        for(int i = 1; i <= n; i++)        {            if(dfn[i] == 0)                Tarjan(i);        }        if(countt == 1)        {            printf("0\n");        }        else        {            for(int i = 1; i <= n; i++)            {                for(int k = head[i]; k != -1; k = edge[k].next)                {                    int j = edge[k].to;                    if(Belong[i] != Belong[j])                    {                        out[Belong[i]]++;                        in[Belong[j]]++;                    }                }            }            //找叶子和根度为0的数量            int root = 0, leaf = 0;            for(int i = 1; i <= countt; i++)            {                if(in[i] == 0) root++;                if(out[i] == 0) leaf++;            }            printf("%d\n", max(root, leaf));        }    }    return 0;}


0 0
原创粉丝点击