Amber <<Play with Trees Solutions>>
半径
只需计算这些相邻点Li,即为最优值了
在这之前,需要去除掉一些点,只保留最外层的那些,才能有上面那样子交点
/**/
/*
n<=200 , m<= 20000的无向图
求一棵生成树,使得任意两点路径中的最大值最小
这题就是求最小直径生成树了MSDT
有Kariv-Hakimi 算法
Amber的Play with Trees Solutions有这道题比较具体的解答
“最短路徑樹不見得是最小直徑生成樹”
absoulte center是边上的一个点(不仅仅是图的顶点),它也是最短最长路径的中点
问题转化为:
求absoulte center,使得它到其他顶点最大值(偏心距)最小
先FLoyd求出任意两点最短路d[u,v]
absoulte center必在某条边,枚举所有边u-v
假设它离u为a,设该点为r,
f(r) = max{min(d[u,x]+a, w[u,v]-a+d[v,x])},则为半径了
至于确定a,Amber的文章上有比较具体的做法了,其实也就是有Kariv-Hakimi 算法了
觉得比较神奇的是枚举边,确定Absolute Center是针对变量a(r离u的距离)作目标的曲线
timus 1569是边权为1时的
*/
#include < iostream >
#include < cstring >
#include < map >
#include < algorithm >
#include < stack >
#include < queue >
#include < cstring >
#include < cmath >
#include < string >
#include < cstdlib >
#include < vector >
#include < cstdio >
#include < set >
#include < list >
#include < numeric >
#include < cassert >
#include < sstream >
#include < ctime >
#include < bitset >
#include < functional >
using namespace std;
const int INF = 0x3f3f3f3f ;
const int MAXN = 501 ;
int w[MAXN][MAXN];
int dist[MAXN][MAXN];
int n, m;
int cu, cv;
int pre[MAXN], in [MAXN];
double dp[MAXN], ans; // 为了更准确点。用了double
void update( int u, int v)
{
vector<pair<int,int> > vt, _vt;
for (int i = 1; i <= n; i++) {
vt.push_back(make_pair(dist[u][i], dist[v][i]));
}
sort(vt.begin(), vt.end());
//按照dist[u,i]从小到大排,最后需要考虑点是dist[u,i] < dist[u,j] && dist[v,i] > dist[u,j], i < j
for (int i = 0; i < vt.size(); i++) {
while(!_vt.empty() && _vt.back().second <= vt[i].second) {
_vt.pop_back();
}
_vt.push_back(vt[i]);
}
int D = INF;//diameter
double a;
if (_vt.size() == 1) {
if (_vt[0].first < _vt[0].second) {
a = 0;
D = 2*_vt[0].first;
} else {
a = w[u][v];
D = 2*_vt[0].second;
}
}else {
for (int i = 1; i < _vt.size(); i++) {
if (D > w[u][v] + _vt[i-1].first + _vt[i].second) {
a = (w[u][v] + _vt[i].second - _vt[i-1].first) / 2.0;
D = w[u][v] + _vt[i-1].first + _vt[i].second;
}
}
}
if (D < ans) {
cu = u;
cv = v;
ans = D;
dp[u] = a;
dp[v] = w[u][v] - a;
}
}
vector < pair < int , int > > edges;
void spfa()
{
fill(pre+1, pre+1+n, -1);
fill(in+1, in+1+n, 0);
for (int i = 1; i <= n; i++) {
if (i != cu && i != cv) {
dp[i] = INF;
}
}
in[cu] = in[cv] = 1;
queue<int> q;
q.push(cu);
q.push(cv);
while (!q.empty()) {
int t = q.front();
q.pop();
in[t] = 0;
for (int i = 1; i <= n; i++) {
if (w[t][i] != INF && dp[i] > dp[t] + w[t][i]) {
dp[i] = dp[t]+w[t][i];
pre[i] = t;
if (!in[i]) {
in[i] = 1;
q.push(i);
}
}
}
}
edges.clear();
for (int i = 1; i <= n; i++) {
if (pre[i] != -1) {
edges.push_back(make_pair(min(i,pre[i]), max(i,pre[i])));
}
}
if (cv != cu) {
edges.push_back(make_pair(min(cu,cv), max(cu,cv)));
}
return ;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in","r",stdin);
#endif
for (;~scanf("%d%d", &n, &m); ) {
fill(w[1], w[n+1], INF);
int a, b, c;
for (int i = 0; i < m; i++) {
scanf("%d%d%d", &a, &b, &c);
w[a][b] = w[b][a] = min(w[a][b], c);
}
for (int i = 1; i <= n; i++) {//?
w[i][i] = 0;
}
memcpy(dist, w, sizeof dist);
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
if (dist[i][k] != INF) {
for (int j = 1; j <= n; j++)
dist[i][j] = min(dist[i][j], dist[i][k]+dist[k][j]);
}
ans = INF;
for (int i = 1; i <= n; i++)
for (int j = i; j <= n; j++){
if (w[i][j] != INF) {
update(i, j);
}
}
cout<<ans<<endl;
spfa();
sort(edges.begin(), edges.end());
for (vector<pair<int,int> >::iterator it = edges.begin() ; it != edges.end(); it++) {
printf("%d %d\n", it->first, it->second);
}
}
return 0;
}
n<=200 , m<= 20000的无向图
求一棵生成树,使得任意两点路径中的最大值最小
这题就是求最小直径生成树了MSDT
有Kariv-Hakimi 算法
Amber的Play with Trees Solutions有这道题比较具体的解答
“最短路徑樹不見得是最小直徑生成樹”
absoulte center是边上的一个点(不仅仅是图的顶点),它也是最短最长路径的中点
问题转化为:
求absoulte center,使得它到其他顶点最大值(偏心距)最小
先FLoyd求出任意两点最短路d[u,v]
absoulte center必在某条边,枚举所有边u-v
假设它离u为a,设该点为r,
f(r) = max{min(d[u,x]+a, w[u,v]-a+d[v,x])},则为半径了
至于确定a,Amber的文章上有比较具体的做法了,其实也就是有Kariv-Hakimi 算法了
觉得比较神奇的是枚举边,确定Absolute Center是针对变量a(r离u的距离)作目标的曲线
timus 1569是边权为1时的
*/
#include < iostream >
#include < cstring >
#include < map >
#include < algorithm >
#include < stack >
#include < queue >
#include < cstring >
#include < cmath >
#include < string >
#include < cstdlib >
#include < vector >
#include < cstdio >
#include < set >
#include < list >
#include < numeric >
#include < cassert >
#include < sstream >
#include < ctime >
#include < bitset >
#include < functional >
using namespace std;
const int INF = 0x3f3f3f3f ;
const int MAXN = 501 ;
int w[MAXN][MAXN];
int dist[MAXN][MAXN];
int n, m;
int cu, cv;
int pre[MAXN], in [MAXN];
double dp[MAXN], ans; // 为了更准确点。用了double
void update( int u, int v)
{
vector<pair<int,int> > vt, _vt;
for (int i = 1; i <= n; i++) {
vt.push_back(make_pair(dist[u][i], dist[v][i]));
}
sort(vt.begin(), vt.end());
//按照dist[u,i]从小到大排,最后需要考虑点是dist[u,i] < dist[u,j] && dist[v,i] > dist[u,j], i < j
for (int i = 0; i < vt.size(); i++) {
while(!_vt.empty() && _vt.back().second <= vt[i].second) {
_vt.pop_back();
}
_vt.push_back(vt[i]);
}
int D = INF;//diameter
double a;
if (_vt.size() == 1) {
if (_vt[0].first < _vt[0].second) {
a = 0;
D = 2*_vt[0].first;
} else {
a = w[u][v];
D = 2*_vt[0].second;
}
}else {
for (int i = 1; i < _vt.size(); i++) {
if (D > w[u][v] + _vt[i-1].first + _vt[i].second) {
a = (w[u][v] + _vt[i].second - _vt[i-1].first) / 2.0;
D = w[u][v] + _vt[i-1].first + _vt[i].second;
}
}
}
if (D < ans) {
cu = u;
cv = v;
ans = D;
dp[u] = a;
dp[v] = w[u][v] - a;
}
}
vector < pair < int , int > > edges;
void spfa()
{
fill(pre+1, pre+1+n, -1);
fill(in+1, in+1+n, 0);
for (int i = 1; i <= n; i++) {
if (i != cu && i != cv) {
dp[i] = INF;
}
}
in[cu] = in[cv] = 1;
queue<int> q;
q.push(cu);
q.push(cv);
while (!q.empty()) {
int t = q.front();
q.pop();
in[t] = 0;
for (int i = 1; i <= n; i++) {
if (w[t][i] != INF && dp[i] > dp[t] + w[t][i]) {
dp[i] = dp[t]+w[t][i];
pre[i] = t;
if (!in[i]) {
in[i] = 1;
q.push(i);
}
}
}
}
edges.clear();
for (int i = 1; i <= n; i++) {
if (pre[i] != -1) {
edges.push_back(make_pair(min(i,pre[i]), max(i,pre[i])));
}
}
if (cv != cu) {
edges.push_back(make_pair(min(cu,cv), max(cu,cv)));
}
return ;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in","r",stdin);
#endif
for (;~scanf("%d%d", &n, &m); ) {
fill(w[1], w[n+1], INF);
int a, b, c;
for (int i = 0; i < m; i++) {
scanf("%d%d%d", &a, &b, &c);
w[a][b] = w[b][a] = min(w[a][b], c);
}
for (int i = 1; i <= n; i++) {//?
w[i][i] = 0;
}
memcpy(dist, w, sizeof dist);
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
if (dist[i][k] != INF) {
for (int j = 1; j <= n; j++)
dist[i][j] = min(dist[i][j], dist[i][k]+dist[k][j]);
}
ans = INF;
for (int i = 1; i <= n; i++)
for (int j = i; j <= n; j++){
if (w[i][j] != INF) {
update(i, j);
}
}
cout<<ans<<endl;
spfa();
sort(edges.begin(), edges.end());
for (vector<pair<int,int> >::iterator it = edges.begin() ; it != edges.end(); it++) {
printf("%d %d\n", it->first, it->second);
}
}
return 0;
}