HDU2767 Proving Equivalences

  • Post author:
  • Post category:其他


Proving Equivalences




Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 6707    Accepted Submission(s): 2327







Problem Description
Consider the following exercise, found in a generic linear algebra textbook.

Let A be an n × n matrix. Prove that the following statements are equivalent:

1. A is invertible.

2. Ax = b has exactly one solution for every n × 1 matrix b.

3. Ax = b is consistent for every n × 1 matrix b.

4. Ax = 0 has only the trivial solution x = 0.

The typical way to solve such an exercise is to show a series of implications. For instance, one can proceed by showing that (a) implies (b), that (b) implies (c), that (c) implies (d), and finally that (d) implies (a). These four implications show that the four statements are equivalent.

Another way would be to show that (a) is equivalent to (b) (by proving that (a) implies (b) and that (b) implies (a)), that (b) is equivalent to (c), and that (c) is equivalent to (d). However, this way requires proving six implications, which is clearly a lot more work than just proving four implications!

I have been given some similar tasks, and have already started proving some implications. Now I wonder, how many more implications do I have to prove? Can you help me determine this?


Input
On the first line one positive number: the number of testcases, at most 100. After that per testcase:

* One line containing two integers n (1 ≤ n ≤ 20000) and m (0 ≤ m ≤ 50000): the number of statements and the number of implications that have already been proved.

* m lines with two integers s1 and s2 (1 ≤ s1, s2 ≤ n and s1 ≠ s2) each, indicating that it has been proved that statement s1 implies statement s2.


Output
Per testcase:

* One line with the minimum number of additional implications that need to be proved in order to prove that all statements are equivalent.


Sample Input
  
  
2 4 0 3 2 1 2 1 3


Sample Output
  
  
4 2

题意:给你定点数和定点之间的关系,关系是单方向的,比如第二个样例3 2表示有三个定点,有两条关系,1 2     1   3    1到2有单向边相连      1到3有单向边相连,问最小添加几条边

使得任意点之间都可到达。我们看题之后可以了解到该题就是问最少添加几条边使得整幅图成为强连通图。



刚拿到这个题我想的是最小的强连通图就是有向环,每个点的出度和入读都分别为1,我就想直接遍历整幅图,求解每个点的出度和入读,当一个点的出度为0时,表示出

度的sum++,当一个点的入度为0时,表示入度的数值summ++,然后max(sum,summ)就是该题的解,然后错了。仔细想了想,如果全部顶点形成两个强连通图,每个顶点的出

度与入度都不是为0,。所以可以把相互有关系的顶点连成一个强连通分量,然后按上述方法求解就行啦。当然有一个特殊的就是当给出的图是一个强连通图的时候,就不需要添

加边啦。

代码展示:

#include<iostream>
#include<vector>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
int N,M;
vector<int> G[20005];
vector<int> rG[20005];
vector<int> vs;
bool used[20005];  //用于查看该点是否被访问
int cmp[20005];   //存储该点对应的连通分量(拓扑序)
int out[20005];   //该连通分量的出度
int in[20005];    //该连通分量的入度
int k;
//初始化
void init()
{
    //因为数值从1 开始,所以所有初始化的长度为N+1
    fill(cmp,cmp+N+1,-1);
    fill(out,out+N+1,0);
    fill(in,in+N+1,0);
    fill(used,used+N+1,false);
    for(int i = 1;i <= N;i++)
    {
        G[i].clear();
        rG[i].clear();
    }
    vs.clear();
}
//dfs遍历正向图并且用后序遍历的方式对顶点进行标号
void dfs(int u)
{
    used[u] = true;
    for(int i = 0;i < G[u].size();i++)
    {
        if(!used[G[u][i]])
        {
            dfs(G[u][i]);
        }
    }
    vs.push_back(u);
}
//dfs遍历逆向图,并且按升序把每个强连通分量标号
void rdfs(int v,int k1)
{
    used[v] = true;
    cmp[v] = k1;
    for(int i = 0;i < rG[v].size();i++)
    {
        if(!used[rG[v][i]])
        {
            rdfs(rG[v][i],k1);
        }
    }
}
void scc()
{
    fill(used,used+N+1,false);
    vs.clear();
    for(int i = 1;i <= N;i++)
    {
        if(!used[i])
        {
            dfs(i);
        }
    }
    //正序遍历图的时候把每个顶点都访问过了
    fill(used,used+N+1,false);
    k = 0;
    for(int i = vs.size()-1;i >= 0;i--)
    {
        if(!used[vs[i]])
        {
            rdfs(vs[i],++k);
        }
    }
    for(int i = 1;i <= N;i++)
    {
        for(int j = 0;j < G[i].size();j++)
        {
            if(cmp[i] == cmp[G[i][j]])
                continue;
            else
            {
                //该联通分量的出度
                out[cmp[i]]++;
                //该联通分量的入度
                in[cmp[G[i][j]]]++;
            }
        }
    }
    int ret = 0;
    int cnt = 0;
    for(int i = 1;i <= k;i++)
    {
        if(out[i] == 0)
        {
            cnt++;
        }
        if(in[i] == 0)
        {
            ret++;
        }
    }
    ret = max(cnt,ret);
    //如果整幅图是一个强连通图,则不需要添加多余的边
    if(k == 1)
        ret = 0;
    printf("%d\n",ret);
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&N,&M);
        int i;
        int a,b;
        init();
        for(i = 1;i <= M;i++)
        {
            scanf("%d%d",&a,&b);
            //添加正向和逆向图
            G[a].push_back(b);
            rG[b].push_back(a);
        }
        scc();
    }
    return 0;
}



版权声明:本文为yedengmei原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。