『最小生成树·玄学随机化』「NOIP2017」宝藏

题目描述

在这里插入图片描述

题解

假如我们要考虑40分(实际上是45)的部分分,我们可以考虑用prim算法来实现。

每次找到深度*权值最小的节点即可。这样对于边权相同的情况下,就一定可以保证是对的。

代码如下:

#include <bits/stdc++.h>

using namespace std;

int n, m, ans = 1e9;
int a[20][20], vis[20], dis[30], dep[30];

struct node {
	int pnt, deep;
};

int bfs(int st)
{
	int ans = 0;
	memset(vis,0,sizeof vis);
	queue <node> q;
	q.push(node{st,1});
	vis[st] = 1;
	while (q.size())
	{
		node x = q.front(); q.pop();
		for (int i=1;i<=n;++i)
			if (vis[i] == 0 && a[x.pnt][i] <= 500000) {
			    vis[i] = 1; ans += a[x.pnt][i] * x.deep;
			    q.push(node{i,x.deep+1});
			}
	}
	return ans;
} 

int main(void)
{
	freopen("treasure.in","r",stdin);
	freopen("treasure.out","w",stdout);
	cin>>n>>m;
	memset(a,30,sizeof a);
	for (int i=1,x,y,v;i<=m;++i)
	{
		cin>>x>>y>>v;
		if (v < a[x][y]) a[x][y] = a[y][x] = v;
	}
	for (int i=1;i<=n;++i)
	    ans = min(ans,bfs(i));
	cout<<ans<<endl;
	return 0;
}

然后我们就发现nnn的范围只有121212,而我们这么做的时间复杂度是O(n2)O(n^2)O(n2),因此比较浪费。

我们发现对于生成树的过程,每一次都会找到一个最小的点,由于最优解不一定会是当前最优解;因此我们每一次可以随机一个序列,用这种思路求解一遍最小生成树的起始点求解即可。

我们可以十分开心的发现,这么做有很大的概率随机到正解,因为n很小,总共的序列也没多少。相当于是在碰碰运气,只不过运气比较好而已。

代码如下:

#include <bits/stdc++.h>

using namespace std;

int n, m, ans = 1e9;
int a[20][20], vis[20], dis[30], dep[30], s[30];

struct node {
	int pnt, deep;
};

int prim(void)
{
	int ans = 0;
	memset(dep,0,sizeof dep);
	memset(vis,0,sizeof vis);
	memset(dis,30,sizeof dis);
	dis[s[1]] = 0, dep[s[1]] = 1;
	for (int i=1;i<=n;++i)
	{
		int now = s[i];
		if (vis[now] == 1 || dis[now] > 500000) return 1e9;
		vis[now] = 1;
		ans += dis[now];
		for (int j=1;j<=n;++j)
		    if (a[now][j] <= 500000 && vis[j] == 0 && a[now][j]*dep[now] < dis[j])
			    dis[j] = a[now][j] * dep[now], dep[j] = dep[now]+1;
	}
	return ans;
}

int main(void)
{
	freopen("treasure.in","r",stdin);
	freopen("treasure.out","w",stdout);
	double begin = clock();
	cin>>n>>m;
	memset(a,30,sizeof a);
	for (int i=1,x,y,v;i<=m;++i)
	{
		cin>>x>>y>>v;
		if (v < a[x][y]) a[x][y] = a[y][x] = v;
	}
	for (int i=1;i<=n;++i) s[i] = i;
	int T = 500000;
	while (T --)
	{
		random_shuffle(s+1,s+n+1);
		ans = min(ans,prim());
	}
	cout<<ans<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值