acwing 图的深度搜索DFS

写目录

    • 邻接表的构建
    • 邻接表DFS
    • AcWing 846. 树的重心
      • 无向图
    • pat 1034 Head of a Gang
      • 无向图/有向图的深度搜索,各连通块分别搜索
      • 有向图版
      • 无向图版

邻接表的构建

acwing 图的深度搜索DFS_第1张图片

邻接表DFS

const int N = 1e5 + 10, M = 2*N;
int h[N], e[M], ne[M];          // h[N]: 顶点Ni的第一个连接点
bool visited[M];                // 某一连接点是否已被搜索
int n = 0, idx = 0;

void add(int a, int b)  // 添加一条边a->b
{
    e[idx] = b;     // 创建相连点b
    ne[idx] = h[a]; // 该相连点b的下一点是当前顶点a的第一个连接点
    h[a] = idx++;   // 更新顶点a的第一个连接点
}
void dfs(int u)
{
    cout << u << " ";
    visited[u] = true;
    for(int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if(!visited[j])
            dfs(j);
    }
}

int main()
{
    memset(h,-1,sizeof h);          // 将h置为-1,表示初始时
    cin >> n;                       // 所有顶点都没有连接点
    int a, b;
    for(int i = 0; i < n - 1; ++i) // n - 1条无向边
    {
        cin >> a >> b;
        add(a, b);
        add(b, a);
    }
    dfs(1);
    return 0;
}

AcWing 846. 树的重心

无向图

  • 主要找删除某个顶点后各连通块中点个数量的关系
  • 连通点数是个数
const int N = 1e5 + 10, M = 2*N;
int h[N], e[M], ne[M];          // h[N]: 顶点Ni的第一个连接点
bool visited[M];                // 某一连接点是否已被搜索
int n = 0, idx = 0;

void add(int a, int b)  // 添加一条边a->b
{
    e[idx] = b;     // 创建相连点b
    ne[idx] = h[a]; // 该相连点b的下一点是当前顶点a的第一个连接点
    h[a] = idx++;   // 更新顶点a的第一个连接点
}

int ans = N;

int dfs(int u)
{
    visited[u] = true;
    
    int sum = 1; // 当前顶点的所有连接点个数(包含当前顶点)
    int ret = 0; // 当前顶点各支路的连接点个数的最大值
    for(int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if(!visited[j])
        {
            int tmp = dfs(j);
            ret = max(ret, tmp);
            sum += tmp;
        }
    }
    ret = max(ret, n - sum); // 因为sum包含当前顶点,所以不用-1
    ans = min(ans, ret);
    return sum;
}

int main()
{
    memset(h,-1,sizeof h);          // 将h置为-1,表示初始时
    cin >> n;                       // 所有顶点都没有连接点
    int a, b;
    for(int i = 0; i < n - 1; ++i) // n - 1条无向边
    {
        cin >> a >> b;
        add(a, b);
        add(b, a);
    }
    
    dfs(1);
    cout << ans;
    return 0;
}

pat 1034 Head of a Gang

无向图/有向图的深度搜索,各连通块分别搜索

  • 各连通块即各帮派
  • 这题有向图无向图都能做
  • 差别是无向图会将通话时间加倍,所以判断时要将总时长 / 2

有向图版

const int N = 2*(1e3 + 10);
unordered_map<string, int> index;
unordered_map<int, string> index_name;
vector<int> each_time(N);
vector<Node> calls[N];
vector<Node> ret;
bool visited[N];
int n, m, idx = 0;
vector<string> start;
struct Node
{
    Node(){}
    Node(string n, int t):name(n), time(t){}
    string name;
    int time;
};
// 在一个连通块内进行的深度搜索
void dfs(int u, int &nsum, int &tsum, int &head)
{
    visited[u] = true;
    if(each_time[u] > each_time[head])        // 更新头目
        head = u;
    for(int i = 0; i < calls[u].size(); ++i)
    {
        int j = index[calls[u][i].name];
        tsum += calls[u][i].time;            // 累加连通块内总通话时长
        if(!visited[j])
        {
            nsum += 1;    // 连通块内成员数,这里需区分是否已经搜索
            dfs(j, nsum, tsum, head);
        }
    }
}

// 检测s是否曾出现过,并设置编号
bool check_in(string s, int &ci)
{
    if(index.count(s))
        ci = index[s];
    else
    {
        ci = idx;
        index_name[ci] = s;
        index[s] = idx++;
    }
}

void add(string s1, string s2, int t)
{
    Node m_node;
    m_node.time = t;
    m_node.name = s2;
    int i1 = 0, i2 = 0;
    check_in(s1, i1);
    check_in(s2, i2);
    calls[i1].push_back(m_node);
    // 为通话双方都加上通话时间
    // 方便后续得到某个成员的总通话时长
    each_time[i1] += t;
    each_time[i2] += t;
}
bool cmp(Node n1, Node n2)
{
    return n1.name < n2.name;
}
int main()
{
    cin >> n >> m;
    string s1, s2;
    int t;
    for(int i = 0; i < n; i++)
    {
        cin >> s1 >> s2 >> t;
        // 如果s1 s2都未出现,则为新的连通块
        if(!index.count(s1) && !index.count(s2))
            start.push_back(s1);
        // 插入邻接表
        add(s1, s2, t);
    }
    for(auto &s : start)
    {
        int head = index[s];
        int tsum = 0, nsum = 1;
        dfs(head, nsum, tsum, head);
        if(tsum > m && nsum > 2)
            ret.push_back({index_name[head], nsum});
    }
    sort(ret.begin(), ret.end(), cmp);    // 姓名字典序排序
    cout << ret.size();
    for(auto &p : ret)
        cout << endl << p.name << " " << p.time;
    return 0;
}

无向图版

void add(string s1, string s2, int t)
{
	...
	// 为通话双方都加上通话时间
    // 方便后续得到某个成员的总通话时长
    each_time[i1] += t;   // 只累加到前者的时长
}
int main()
{
	...
		// 插入邻接表
        add(s1, s2, t);
        add(s2, s1, t);
	...
		if(tsum / 2 > m && nsum > 2)  // 总时长 / 2
            ret.push_back({index_name[head], nsum});
    ...
}


你可能感兴趣的:(深度优先,图论,算法)