题目描述
题解
假如我们要考虑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;
}