深度优先搜索解密人际关系:计算握手次数的算法探索

179 阅读4分钟

题目

目标

了解图的遍历和递归算法的应用。通过构建人际关系图,然后使用深度优先搜索(DFS)进行遍历,判断陌生人之间是否满足握手条件,从而计算握手次数。

题目描述

算上扶苏,本次派对共有 n 个人。但是,并不是任何两个人都互相认识,并且互相认识的人关系也未必好。 具体而言,任意两个人可能是如下三种关系之一:

1.敌人
2.朋友
3.陌生人

派对的一大重要活动是相互握手。对任意两个人 么,u,他们之间的握手情况遵循下面的规则:

1.如果 u和 u 是朋友关系,那么他们一定握手一次。
2.如果 u和 u 是敌人关系,那么他们一定不握手。
3.如果u和u 是陌生人关系,且存在一个人 w,使得w是u和u之一的朋友,同时是 u,u 中另一人的敌人,则 u和u 不会握手,否则 u和 u 一定握手一次。

对第三条规则,简单的说法是:一对陌生人之间,如果某一方的朋友是另一方的敌人,则不握手,否则握手。

已知共有 p 对人是朋友关系,g 对人是敌人关系。除了这p+ g 对人,其他每对人均为陌生人关系请你求出本次派对一共握手了多少次。

输入格式:

第一行是三个整数,依次表示参加派对的人数 n,朋友关系的条数 p 和敌人关系的条数 q。
接下来 p 行,每行两个整数 u,u,表示u和 u 是朋友关系。
接下来 q 行,每行两个整数 u,u,表示 u和 u 是敌人关系。

输出格式:

输出一行一个整数,表示本次派对的握手次数,

输入/输出示例

输入示例:

4 2 2
1 2
2 3
1 4
1 3

输出示例:

3

解题方法

思路说明

解题思路如下:

  1. 首先,我们需要建立一个图来表示人与人之间的关系。每个人可以表示为图中的一个节点,而朋友关系和敌人关系可以表示为图中的边。

  2. 我们使用两个哈希映射来存储朋友关系和敌人关系。其中,朋友关系的映射中,键表示一个人,值表示他的朋友;敌人关系的映射中,键表示一个人,值表示他的敌人。

  3. 接下来,我们根据朋友关系的映射构建图的邻接表表示。遍历朋友关系的映射,对于每对朋友关系 (u, v),我们将 u 和 v 加入彼此的邻接表中。

  4. 然后,我们使用深度优先搜索(DFS)来遍历图。从每个未访问过的节点开始,进行DFS遍历。在DFS过程中,我们检查陌生人之间的握手条件:如果某一方的朋友是另一方的敌人,则不握手,否则握手一次。

  5. 在DFS过程中,每次满足握手条件时,将握手次数加1,并继续对下一个节点进行DFS遍历。

  6. 最后,得到的握手次数即为本次派对的握手次数。

通过以上步骤,我们找到所有满足握手条件的节点对,并计算握手次数。

代码实现

package com.lgf.demo;

import java.util.Scanner;

public class PartyHandshakes {

    public static void main(String[] args) {
        long count = 0;
        int people, Friend, enemy;
        Scanner scanner = new Scanner(System.in);
        people = scanner.nextInt();
        Friend = scanner.nextInt();
        enemy = scanner.nextInt();

        int[][] relation = new int[people][people];
        // 初始化关系矩阵
        for (int i = 0; i < people; i++) {
            for (int j = 0; j < people; j++) {
                relation[i][j] = 0;
            }
            relation[i][i] = -1;
        }

        // 读取朋友关系
        for (int f = 0, row, column; f < Friend; f++) {
            row = scanner.nextInt();
            column = scanner.nextInt();
            relation[row - 1][column - 1] = relation[column - 1][row - 1] = 1;
        }

        // 读取敌人关系
        for (int e = 0, row, column; e < enemy; e++) {
            row = scanner.nextInt();
            column = scanner.nextInt();
            relation[row - 1][column - 1] = relation[column - 1][row - 1] = 2;
        }

        // 遍历所有人,计算握手次数
        for (int p = 0; p < people - 1; p++) {
            for (int it = p + 1; it < people; it++) {
                if (relation[p][it] == 0) {
                    // 如果是陌生人关系,则判断是否满足握手条件
                    if (isStrangerHandshakePossible(relation, p, it)) {
                        count++;
                    }
                } else if (relation[p][it] == 1) {
                    // 如果是朋友关系,则一定握手
                    count++;
                }
            }
        }

        System.out.println(count);
    }

    // 判断两个陌生人之间是否满足握手条件
    private static boolean isStrangerHandshakePossible(int[][] relation, int person1, int person2) {
        boolean handshakePossible = true;
        for (int i = 0; i < relation.length; i++) {
            if (i != person1 && i != person2) {
                if (relation[person1][i] == 1 && relation[person2][i] == 2) {
                    // 如果其中一个人的朋友是另一个人的敌人,则不满足握手条件
                    return false;
                }
            }
            if (relation[person1][i] == 0 && i != person2) {
                // 如果是陌生人关系,则尝试进行握手并递归判断后续握手情况
                relation[person1][i] = relation[i][person1] = 1;
                handshakePossible = handshakePossible && isStrangerHandshakePossible(relation, i, person2);
                relation[person1][i] = relation[i][person1] = 0;
            }
        }

        return handshakePossible;
    }
}