题目描述
原题链接:2959.关闭分部的可行集合数目
解题思路
考虑暴力枚举每一种关闭分部的可能性,也就是枚举每一种分部开着的可能性。我们假设以二进制的1表示分部开着,0表示分部关闭。以一个十位的二进制数来表示这n个分部的开关情况。
由于题目的限制
1
≤
n
≤
10
1\leq n \leq 10
1≤n≤10,因此最多有
2
10
=
1024
2^{10}=1024
210=1024种情况。而枚举每一种情况又需要进行一次Floyd算法,因此整体的时间复杂度是
o
(
2
n
∗
n
3
)
o(2^n*n^3)
o(2n∗n3)。带入
n
=
10
n=10
n=10,最多也就是
1
0
6
10^6
106左右,可以过掉。
总的来说,我们先初始化已知边,由于两个点之间刚开始可能有多条边,因此我们只需要保存距离最小的边。之后我们枚举每一种情况,用一个tool[i]
数组来表示第i
个分部是否被选中。我们对每一种情况都使用一次Floyd算法,注意这时候我们需要判断一下中间点k
是否被选中,如果没有判断则可能会更新所选中分布之间的距离。最后我们需要看看被选中的分部之间的距离是不是都小于maxDistence
。
C++代码
Floyd算法代码
void floyd(vector<vector<int>>& d)
{
for (int k = 0; k < d.size(); k ++ ){
if(tool[k] == 0) continue;
for (int i = 0; i < d.size(); i ++ ) {
for (int j = 0; j < d.size(); j ++ ) {
d[i][j] = d[j][i] = min(d[i][j], d[i][k] + d[k][j]);
}
}
}
}
要注意这里的d[i][j] = d[j][i] = min(d[i][j], d[i][k] + d[k][j]);
必须要给两个方向的都赋值,因为本题是一个无向图。如果只给一个方向赋值则会造成答案错误,我之前就在这里卡了好久TAT。
完整AC代码
class Solution {
public:
int tool[11];
void floyd(vector<vector<int>>& d)
{
for (int k = 0; k < d.size(); k ++ ){
if(tool[k] == 0) continue;
for (int i = 0; i < d.size(); i ++ ) {
for (int j = 0; j < d.size(); j ++ ) {
d[i][j] = d[j][i] = min(d[i][j], d[i][k] + d[k][j]);
}
}
}
}
int numberOfSets(int n, int maxDistance, vector<vector<int>>& roads) {
int cnt = 0;
for(int i = 0; i < 1 << n; i++)
{
memset(tool, 0, sizeof(tool));
for(int j = 0; j < n; j++)
{
if(((i >> j) & 1) == 1)
{
tool[j] = 1;
}
}
vector<vector<int>> d;
d.resize(n, vector<int>(n, 1010));
for(int j = 0; j < n; j++) d[j][j] = 0;
for(int j = 0; j < roads.size(); j++)
{
int x = roads[j][0];
int y = roads[j][1];
int dis = roads[j][2];
d[x][y] = d[y][x] = min(d[x][y], dis);
}
floyd(d);
bool flag = true;
for(int j = 0; j < n; j++)
{
for(int k = j; k < n; k++)
{
if(tool[j] == 1 && tool[k] == 1)
{
if(maxDistance < d[j][k]) {
flag = false;
break;
}
}
}
if(!flag) {
break;
}
}
if(flag) {
cnt++;
}
}
return cnt;
}
};