试题编号: | 201903-5 |
试题名称: | 317号子任务 |
时间限制: | 1.0s |
内存限制: | 512.0MB |
问题描述: |
|
看完题目,感觉是多源最短路径问题,最先想到的是对每个据点利用Dijkstra算法找到最近K个行星发动机的最短路径和,而这里有存在据点能到达行星发动机的数量不足K个的情况,因此在这里又加入了并查集对据点进行处理,将每一集和中存在的行星发动机数量保存在父节点中,即可通过查找每一据点的父节点知道能到达的行星发动机数量,进而进行Dijkstra算法。
代码实现后为超时0分。具体如下:
#include
注意的是,题目中提到的可能出现重边及自环,重边即两条边的起点和终点都对应相同 ;自环,即存在某一条边的起点和终点是同一个点。因此在数据的输入获取上不能使用map结构。
为了避免超时,这里程序修改了一下,不再对所有据点进行Dijkstra搜索,而是仅对行星发动机据点进行搜索,最后对每一据点进行判断来实现解答。程序如下,运行超时,30分。
#include "pch.h"
#include
#include
#define INF (0x3f3f3f3f)
using namespace std;
int N, M, K;
bool Lei[10001]; //据点类型,1代表为行星发动机
struct Dian {
mapdian; //该点可连通的点及对应长度
//bool lei; //
};
vector>Judian[10001];
struct Bian {
int to;
long long lenth;
Bian(int too, int lenthh) {
to = too; lenth = lenthh;
}
bool operator<(const Bian S)const {
return lenth > S.lenth; //最小堆
}
};
//Bian Road[10001];
priority_queueSave;
//并查集(union-find sets)
int Par[10001], Rank[10001], Num_Xx[10001]; //父节点,树深度, 行星发动机数量
//查找一个元素所在的集合
int Find_Set(int x)
{
if (Par[x] == x)
return x;
else
return Find_Set(Par[x]);
}
//查找两个元素是否为同一集合,若是返回,不是则将两个元素所属集合合并
void Union(int x, int y)
{
x = Find_Set(x);
y = Find_Set(y);
if (x == y)
return;
Par[x] = y; //两个集合合并
Num_Xx[y] = Num_Xx[x] + Num_Xx[y]; //将两个集合的行星发动机数量相加
}
bool Flag[10005]; //通过0/1区分是否找到
int Dis[10005][10005];
//从start行星发动机据点出发,找到到所有据点的最短路径
void Dijkstra(int Start)
{
while (!Save.empty())
{
Save.pop();
}
memset(Flag, 0, sizeof(Flag));
//memset(Add, INF, sizeof(Add));
Dis[Start][Start] = 0;
//Add[Start] = 0;
Save.push(Bian(Start, Dis[Start][Start]));
while (!Save.empty())
{
Bian Front = Save.top(); //上一个到达的点
Save.pop();
if (Flag[Front.to])
continue;
Flag[Front.to] = 1;
for (int i = 0; i < Judian[Front.to].size(); i++)
{
Bian E = Bian(Judian[Front.to][i].first, Judian[Front.to][i].second);
if (!Flag[E.to])
{
if (Dis[Start][E.to] > Dis[Start][Front.to] + E.lenth)
{
Dis[Start][E.to] = Dis[Start][Front.to] + E.lenth;
//Add[E.to] = E.lenth;
Save.push(Bian(E.to, Dis[Start][E.to]));
}
}
}
}
return;
}
int Xx_Judian[10001];
int main()
{
cin >> N >> M >> K; //N个样例,M个进程
cin.ignore();
//memset(Dis, INF, sizeof(Dis));
int count = 0; //表示行星发动机的数量
memset(Dis, INF, sizeof(Dis));
for (int i = 1; i < N + 1; i++)
{
cin >> Lei[i];
Par[i] = i; //初始化并查集
Rank[i] = 0;
Num_Xx[i] = Lei[i];
if (Lei[i])
Xx_Judian[count++] = i; //表示行星发动机
}
for (int i = 1; i < M + 1; i++)
{
int a, b, c;
cin >> a >> b >> c;
//cout << a << b << c << endl;
Judian[a].push_back({ b,c });
Judian[b].push_back({ a,c });
Union(a, b); //j将a、b两个据点合并
}
for (int i = 0; i < count; i++)
{
//cout << i << ": " << Xx_Judian[i] << endl;
Dijkstra(Xx_Judian[i]);
}
int cal[10001];
for (int i = 1; i < N + 1; i++)
{
int cnt = 0;
for (int j = 1; j < count + 1; j++)
{
if (Find_Set(i) == Find_Set(Xx_Judian[j - 1])) //若行星发动机与据点属于同意集合
{
//cout << "i = " << i << "xx=" << Xx_Judian[j - 1] << endl;
cal[cnt++] = Dis[Xx_Judian[j - 1]][i];
}
}
sort(cal, cal + cnt);
int result = 0;
for (int j = 0; j < min(K, cnt); j++)
{
result += cal[j];
}
cout << result << endl;
}
return 0;
}