201903-5 317号子任务(并查集和Dijkstra最短路径)

试题编号: 201903-5
试题名称: 317号子任务
时间限制: 1.0s
内存限制: 512.0MB
问题描述:

201903-5 317号子任务(并查集和Dijkstra最短路径)_第1张图片
201903-5 317号子任务(并查集和Dijkstra最短路径)_第2张图片
201903-5 317号子任务(并查集和Dijkstra最短路径)_第3张图片

 

     看完题目,感觉是多源最短路径问题,最先想到的是对每个据点利用Dijkstra算法找到最近K个行星发动机的最短路径和,而这里有存在据点能到达行星发动机的数量不足K个的情况,因此在这里又加入了并查集对据点进行处理,将每一集和中存在的行星发动机数量保存在父节点中,即可通过查找每一据点的父节点知道能到达的行星发动机数量,进而进行Dijkstra算法。

     代码实现后为超时0分。具体如下:

#include   
#include   
#include   
#include   
#include   
#include   
#include   
#include  
#include  
#include  
#include 
#include 
#include 
#include
#define INF (0x3f3f3f3f)

using namespace std;
int  N, M, K;
bool Lei[10001];		//据点类型,1代表为行星发动机
struct Dian {
	mapdian;	//该点可连通的点及对应长度
	//bool lei;	//
};
vectorJudian(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区分是否找到
long long Dis[10005];

//从start据点出发,找到最近的K个行星发动机,返回最短距离和
int Dijkstra(int Start, int K)	
{
	int Count = 0, result = 0;
	while (!Save.empty())
	{
		Save.pop();
	}
	memset(Flag, 0, sizeof(Flag));
	memset(Dis, INF, sizeof(Dis));
	//memset(Add, INF, sizeof(Add));
	Dis[Start] = 0;
	//Add[Start] = 0;
	Save.push(Bian(Start, Dis[Start]));
	while (!Save.empty())
	{
		Bian Front = Save.top();	//上一个到达的点
		Save.pop();
		//if (Front.to == To)
		//	return;
		if (Flag[Front.to])
			continue;
		if (Lei[Front.to] == 1)	//如果
		{
			Count++;
			result += Dis[Front.to];
			if (Count >= K)
				return result;
		}
		Flag[Front.to] = 1;
		map::iterator it;
		for (it = Judian[Front.to].dian.begin(); it != Judian[Front.to].dian.end(); it++)
		{
			Bian E = Bian(it->first, it->second);
			if (!Flag[E.to])
			{
				if (Dis[E.to] > Dis[Front.to] + E.lenth)
				{
					Dis[E.to] = Dis[Front.to] + E.lenth;
					//Add[E.to] = E.lenth;
					Save.push(Bian(E.to, Dis[E.to]));
				}
				
			}
		}
	}
	return -1;
}
int main()
{
	cin >> N >> M >> K;	//N个样例,M个进程
	cin.ignore();

	for (int i = 1; i < N + 1; i++)
	{
		cin >> Lei[i];
		Par[i] = i;		//初始化并查集
		Rank[i] = 0;
		Num_Xx[i] = Lei[i];
	}
	for (int i = 1; i < M + 1; i++)
	{
		int a, b, c;
		cin >> a >> b >> c;
		//cout << a << b << c << endl;
		Judian[a].dian[b] = c;
		Judian[b].dian[a] = c;
		Union(a, b);	//j将a、b两个据点合并
	}
	for (int i = 1; i < N + 1; i++)
	{
		int father = Find_Set(i);
		if (Num_Xx[father] >= K)
			cout << Dijkstra(i, K) << endl;
		else
			cout << Dijkstra(i, Num_Xx[father]) << endl;
	}
	return 0;
}

      注意的是,题目中提到的可能出现重边及自环,重边即两条边的起点和终点都对应相同 ;自环,即存在某一条边的起点和终点是同一个点。因此在数据的输入获取上不能使用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;
}

 

你可能感兴趣的:(CSP,认证,并查集,Dijkstra)