2013-team4/code/mdst
从 Trac 迁移的文章
这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。
原文章内容如下:
{{{
最小直径生成树(MDST):一个无向图G所有生成树中,直径最小的生成树,可能有多个
做法:以绝对中心当作起点生成最短路树SPT,这个SPT就是一个MDST
求绝对中心的算法:Kariv-Hakimi算法,具体算法描述参考这里:http://www.csie.ntnu.edu.tw/~u91029/Path3.html#6
}}}
{{{
#include <queue>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int MAXN=200+10;
const int inf=1e9;
double dis[MAXN];
queue<int> q;
int map[MAXN][MAXN], d[MAXN][MAXN], rk[MAXN][MAXN];
int pre[MAXN], vis[MAXN];
int n, m, s1, s2;
int cur;
void floyd() {
for (int k=0; k<n; k++)
for (int i=0; i<n; i++)
for (int j=0; j<n; j++)
d[i][j]=min(d[i][j], d[i][k]+d[k][j]);
}
bool cmp(const int &a, const int &b) {
return d[cur][a]<d[cur][b];
}
void Kariv_Hakimi() {
for (int i=0; i<n; i++) {
for (int j=0; j<n; j++) rk[i][j]=j;
cur=i; sort(rk[i], rk[i]+n, cmp);
}
int ret=inf; s1=s2=-1;
for (int u=0; u<n; u++) {
if (d[u][rk[u][n-1]]*2<ret) {
ret=d[u][rk[u][n-1]]*2;
s1=s2=u; dis[s1]=0;
}
for (int v=0; v<n; v++)
if (map[u][v]!=-1) {
for (int k=n-1, i=n-2; i>=0; i--)
if (d[v][rk[u][i]]>d[v][rk[u][k]]) {
int tmp=d[u][rk[u][i]]+d[v][rk[u][k]]+map[u][v];
if (tmp<ret) {
ret=tmp; s1=u; s2=v;
dis[s1]=(double)tmp/2-d[u][rk[u][i]];
dis[s2]=(double)map[u][v]-dis[s1];
}
k=i;
}
}
}
printf("%d\n", ret);
}
void spfa() {
memset(vis, 0, sizeof(vis));
memset(pre, -1, sizeof(pre));
for (int i=0; i<n; i++) if (i!=s1&&i!=s2) dis[i]=inf;
while (!q.empty()) q.pop();
q.push(s1); vis[s1]=true;
if (s1!=s2) {
q.push(s2); vis[s2]=true; pre[s2]=s1;
}
while (!q.empty()) {
int u=q.front(); q.pop(); vis[u]=false;
for (int v=0; v<n; v++)
if (map[u][v]!=-1&&dis[v]>dis[u]+map[u][v]) {
dis[v]=dis[u]+map[u][v]; pre[v]=u;
if (!vis[v]) {
vis[v]=true;
q.push(v);
}
}
}
}
void solve() {
floyd();
Kariv_Hakimi();
spfa();
for (int i=0; i<n; i++)
if (pre[i]!=-1) {
if (i<pre[i]) printf("%d %d\n", i+1, pre[i]+1);
else printf("%d %d\n", pre[i]+1, i+1);
}
}
void init() {
for (int i=0; i<n; i++) {
d[i][i]=0;
for (int j=i+1; j<n; j++) d[i][j]=d[j][i]=inf;
}
memset(map, -1, sizeof(map));
}
int main() {
scanf("%d%d", &n, &m);
init();
for (int u, v, w, i=0; i<m; i++) {
scanf("%d%d%d", &u, &v, &w); u--; v--;
if (u==v) continue;
if (w<d[u][v]) {
d[u][v]=d[v][u]=w;
map[u][v]=map[v][u]=w;
}
}
solve();
return 0;
}
}}}
最小直径生成树(MDST):一个无向图G所有生成树中,直径最小的生成树,可能有多个
做法:以绝对中心当作起点生成最短路树SPT,这个SPT就是一个MDST
求绝对中心的算法:Kariv-Hakimi算法,具体算法描述参考这里:http://www.csie.ntnu.edu.tw/~u91029/Path3.html#6
#include <queue>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int MAXN=200+10;
const int inf=1e9;
double dis[MAXN];
queue<int> q;
int map[MAXN][MAXN], d[MAXN][MAXN], rk[MAXN][MAXN];
int pre[MAXN], vis[MAXN];
int n, m, s1, s2;
int cur;
void floyd() {
for (int k=0; k<n; k++)
for (int i=0; i<n; i++)
for (int j=0; j<n; j++)
d[i][j]=min(d[i][j], d[i][k]+d[k][j]);
}
bool cmp(const int &a, const int &b) {
return d[cur][a]<d[cur][b];
}
void Kariv_Hakimi() {
for (int i=0; i<n; i++) {
for (int j=0; j<n; j++) rk[i][j]=j;
cur=i; sort(rk[i], rk[i]+n, cmp);
}
int ret=inf; s1=s2=-1;
for (int u=0; u<n; u++) {
if (d[u][rk[u][n-1]]*2<ret) {
ret=d[u][rk[u][n-1]]*2;
s1=s2=u; dis[s1]=0;
}
for (int v=0; v<n; v++)
if (map[u][v]!=-1) {
for (int k=n-1, i=n-2; i>=0; i--)
if (d[v][rk[u][i]]>d[v][rk[u][k]]) {
int tmp=d[u][rk[u][i]]+d[v][rk[u][k]]+map[u][v];
if (tmp<ret) {
ret=tmp; s1=u; s2=v;
dis[s1]=(double)tmp/2-d[u][rk[u][i]];
dis[s2]=(double)map[u][v]-dis[s1];
}
k=i;
}
}
}
printf("%d\n", ret);
}
void spfa() {
memset(vis, 0, sizeof(vis));
memset(pre, -1, sizeof(pre));
for (int i=0; i<n; i++) if (i!=s1&&i!=s2) dis[i]=inf;
while (!q.empty()) q.pop();
q.push(s1); vis[s1]=true;
if (s1!=s2) {
q.push(s2); vis[s2]=true; pre[s2]=s1;
}
while (!q.empty()) {
int u=q.front(); q.pop(); vis[u]=false;
for (int v=0; v<n; v++)
if (map[u][v]!=-1&&dis[v]>dis[u]+map[u][v]) {
dis[v]=dis[u]+map[u][v]; pre[v]=u;
if (!vis[v]) {
vis[v]=true;
q.push(v);
}
}
}
}
void solve() {
floyd();
Kariv_Hakimi();
spfa();
for (int i=0; i<n; i++)
if (pre[i]!=-1) {
if (i<pre[i]) printf("%d %d\n", i+1, pre[i]+1);
else printf("%d %d\n", pre[i]+1, i+1);
}
}
void init() {
for (int i=0; i<n; i++) {
d[i][i]=0;
for (int j=i+1; j<n; j++) d[i][j]=d[j][i]=inf;
}
memset(map, -1, sizeof(map));
}
int main() {
scanf("%d%d", &n, &m);
init();
for (int u, v, w, i=0; i<m; i++) {
scanf("%d%d%d", &u, &v, &w); u--; v--;
if (u==v) continue;
if (w<d[u][v]) {
d[u][v]=d[v][u]=w;
map[u][v]=map[v][u]=w;
}
}
solve();
return 0;
}