CodeForces 986A-Fair 最短路

题目大意
在无向无权连通图G=中,每一个结点都有一个不超过k的编号(保证1~k每个编号至少出现一次)。试求从每一个结点出发,遍历到k个编号中的s个所需要经过的最短路径之和。

数据范围
1<=n<=1e+5, 1<=m<=1e+5, 1<=k<=min(n,100), 1<=s<=k.

乍一看,这个题的目标是求最短路径,或许可以直接对于每一个点来求一次 遍历到k个编号中的s个所需要经过的最短路径之和。但是!事实上是不可以的。这种做法的时间复杂度,最坏情况很明显是O(n(n+m)),在 1<=n<=1e+5, 1<=m<=1e+5的规模下肯定是无法在2s的时限内得到结果的。

我们试图求出从起点到找到剩余s-1个编号的最短距离之和,利用贪婪策略,最短距离和肯定是到其他编号前s-1短的距离之和。由此,不妨可以反过来,我们以每一个编号为基准,遍历求出从每一个结点出发,找到这个编号的最短距离。对每个结点求解的时候将这个结点到其他的编号的最短距离排序,取前s个编号求和即可。

此算法的时间复杂度为O(k(m+n)+nklgk)。
具体代码如下:(诚信所向,请勿直接抄袭)
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

int n, m, k, s;
vector G[100008];
int dist[100008][108];
int a[100008];

int main()
{
	memset(dist, -1, sizeof(dist));
	scanf ("%d %d %d %d", &n, &m, &k, &s);
	for (int i = 1; i <= n; i ++)
	{
		scanf ("%d", &a[i]);
	}
	for (int i = 1; i <= m; i ++)
	{
		int x, y;
		scanf ("%d %d", &x, &y);
		G[x].push_back(y);
		G[y].push_back(x);
	}
	for (int i = 1;i <= k; i ++)
	{
		queue q;
		for (int j = 1; j <= n; j ++)
		{
			if (a[j] == i)
			{
				dist[j][i] = 0;
				q.push(j);
			}
		}
		while (!q.empty())
		{
			int x = q.front(); q.pop();
			int siz = G[x].size();
			for (unsigned j = 0; j < siz; j ++)
			{
				int k = G[x][j];
				if (dist[k][i] == -1)
				{
					dist[k][i] = dist[x][i] + 1;
					q.push(k);
				}
			}
		}
	}
	for (int i = 1; i <= n; i ++)
	{
		sort(dist[i] + 1, dist[i] + k + 1);
		int ans = 0;
		for (int j = 1; j <= s; j ++)
		{
			ans += dist[i][j];
		}
		printf ("%d", ans);
		if (i != n) printf (" "); else printf ("\n");
	}
	return 0;
}


你可能感兴趣的:(CodeForces 986A-Fair 最短路)