问题描述
小U加入了一个团队,团队决定举办分布式团建活动来加强新老成员间的交流。为了最大化新旧成员间的互动,需要将团队成员分成若干个由三人组成的小组,使得每个小组内成员间的熟悉程度之和最小。每个小组的熟悉程度定义为小组内成员两两之间的熟悉程度之和。小U负责计算出最优的分组方式。
矩阵familiar_matrix[i][j]表示第i个人和第j个人之间的熟悉程度。保证两人彼此的熟悉程度是一样的。
保证人数一定是3的倍数。
测试样例
样例1:
输入:
N = 3,familiar_matrix = [[100, 78, 97], [78, 100, 55], [97, 55, 100]]
输出:230
样例2:
输入:
N = 6,familiar_matrix = [[100, 56, 19, 87, 38, 61], [56, 100, 70, 94, 88, 94], [19, 70, 100, 94, 43, 95], [87, 94, 94, 100, 85, 11], [38, 88, 43, 85, 100, 94], [61, 94, 95, 11, 94, 100]]
输出:299
样例3:
输入:
N = 9,familiar_matrix = [[100, 24, 35, 45, 56, 67, 78, 89, 90], [24, 100, 45, 56, 67, 78, 89, 90, 11], [35, 45, 100, 67, 78, 89, 90, 11, 12], [45, 56, 67, 100, 89, 90, 11, 12, 13], [56, 67, 78, 89, 100, 11, 12, 13, 14], [67, 78, 89, 90, 11, 100, 13, 14, 15], [78, 89, 90, 11, 12, 13, 100, 15, 16], [89, 90, 11, 12, 13, 14, 15, 100, 17], [90, 11, 12, 13, 14, 15, 16, 17, 100]]
输出:182
回溯
#include <climits>
#include <iostream>
#include <vector>
using namespace std;
void dfs(int N, vector<vector<int>> &familiar_matrix, vector<bool> &used,
int current_sum, int groups_formed, int &min_sum) {
if (groups_formed == N / 3) {
if (current_sum < min_sum) {
min_sum = current_sum;
}
return;
}
// Find the first unused member
int first = -1;
for (int i = 0; i < N; ++i) {
if (!used[i]) {
first = i;
break;
}
}
used[first] = true;
// Try all possible pairs with 'first'
for (int second = first + 1; second < N; ++second) {
if (!used[second]) {
used[second] = true;
for (int third = second + 1; third < N; ++third) {
if (!used[third]) {
used[third] = true;
// Calculate the familiarity of this group
int group_sum = familiar_matrix[first][second] +
familiar_matrix[first][third] +
familiar_matrix[second][third];
dfs(N, familiar_matrix, used, current_sum + group_sum,
groups_formed + 1, min_sum);
used[third] = false;
}
}
used[second] = false;
}
}
used[first] = false;
}
int solution(int N, std::vector<std::vector<int>> familiar_matrix) {
int min_sum = INT_MAX;
vector<bool> used(N, false);
dfs(N, familiar_matrix, used, 0, 0, min_sum);
return min_sum;
}
int main() {
// You can add more test cases here
std::vector<std::vector<int>> familiar_matrix1 = {
{100, 78, 97}, {78, 100, 55}, {97, 55, 100}};
std::vector<std::vector<int>> familiar_matrix2 = {
{100, 56, 19, 87, 38, 61}, {56, 100, 70, 94, 88, 94},
{19, 70, 100, 94, 43, 95}, {87, 94, 94, 100, 85, 11},
{38, 88, 43, 85, 100, 94}, {61, 94, 95, 11, 94, 100}};
std::cout << (solution(3, familiar_matrix1) == 230) << std::endl;
std::cout << (solution(6, familiar_matrix2) == 299) << std::endl;
return 0;
}
dp
#include <vector>
#include <climits>
#include <algorithm>
#include <iostream>
using namespace std;
int solution(int N, vector<vector<int>>& familiar_matrix) {
const int INF = 1e9;
vector<int> dp(1 << N, INF);
dp[0] = 0; // 初始状态:无人被选
for (int mask = 0; mask < (1 << N); mask++) {
if (dp[mask] == INF) continue; // 跳过无效状态
// 找到第一个未被选的人
int first = -1;
for (int i = 0; i < N; i++) {
if (!(mask & (1 << i))) {
first = i;
break;
}
}
if (first == -1) continue; // 所有人已被选
// 尝试所有可能的 3 人组合(包含 first)
for (int second = first + 1; second < N; second++) {
if (mask & (1 << second)) continue; // 已选
for (int third = second + 1; third < N; third++) {
if (mask & (1 << third)) continue; // 已选
int new_mask = mask | (1 << first) | (1 << second) | (1 << third);
int group_sum = familiar_matrix[first][second] +
familiar_matrix[first][third] +
familiar_matrix[second][third];
dp[new_mask] = min(dp[new_mask], dp[mask] + group_sum);
}
}
}
return dp[(1 << N) - 1]; // 所有人被选时的最小和
}
int main() {
// You can add more test cases here
std::vector<std::vector<int>> familiar_matrix1 = {
{100, 78, 97}, {78, 100, 55}, {97, 55, 100}};
std::vector<std::vector<int>> familiar_matrix2 = {
{100, 56, 19, 87, 38, 61}, {56, 100, 70, 94, 88, 94},
{19, 70, 100, 94, 43, 95}, {87, 94, 94, 100, 85, 11},
{38, 88, 43, 85, 100, 94}, {61, 94, 95, 11, 94, 100}};
std::cout << (solution(3, familiar_matrix1) == 230) << std::endl;
std::cout << (solution(6, familiar_matrix2) == 299) << std::endl;
return 0;
}