朱刘算法
求有向图的最小生成树
树形图:
(1)没有环。
(2)除了根之外,每个点的入度为1。
最小树形图问题:求一个树形图,使得总边权最小。
步骤:
(1)对于除了根节点以外的每个点,找出所有入边中,权值最小的边。
(2)选出的边是否存在环。如果不存在累加所有边的权值,直接结束。
(3)如果有环的话,将所有点缩点,构造一个新的图。如果有环的话,不会存在两个相交的环,因为环与环之间是没有公共点的。对于边:
1.环内的边直接删去
2.u->v,v在环内,w[u,v] = w[u,v] - w[v在环内的入边]
3.其它边,保持不变
每一次起码会缩一次点,最多迭代n次,算法一定会终止。
证明:
对于出现环的情况:
1.至少去掉一条边。
2.一定存在一个最优解,只去掉环中的一条边。新加进去边时,要把原来这个点进入的边删除。
#include <bits/stdc++.h>
using namespace std;
#define x first
#define y second
const double INF = 1000000000.0;
const int N = 1005;
int n, m, pre[N];
typedef double db;
db d[N][N], bd[N][N];
pair<db, db> p[N];
bool g[N][N], bg[N][N], vis[N];
db dis(int a, int b)
{
db x = p[a].x - p[b].x;
db y = p[a].y - p[b].y;
return sqrt(x * x + y * y);
}
int tot = 0, scc_cnt = 0, low[N], dfn[N], scc[N];
stack<int> s;
void tarjan(int x)
{
dfn[x] = low[x] = ++ tot, s.push(x), vis[x] = 1;
int i = pre[x];
if(!dfn[i])
{
tarjan(i);
low[x] = min(low[x], low[i]);
}
else if(vis[i]) low[x] = min(low[x], low[i]);
if(low[x] == dfn[x])
{
int y;
scc_cnt ++;
do
{
y = s.top(); s.pop();
vis[y] = 0;
scc[y] = scc_cnt;
}while(y != x);
}
return ;
}
void dfs(int x)
{
vis[x] = 1;
for (int i = 1; i <= n; ++ i)
{
if(g[x][i] && !vis[i]) dfs(i);
}
return ;
}
bool check()
{
dfs(1);
bool pd = true;
for (int i = 1; i <= n; ++ i) if(!vis[i]) {pd = false; break; }
for (int i = 0; i <= n; ++ i) vis[i] = 0;
return pd;
}
db ZL()
{
db res = 0;
for (int i = 1; i <= n; ++ i)
{
for (int j = 1 ; j <= n; ++ j)
{
if(g[i][j]) d[i][j] = dis(i, j);
else d[i][j] = INF;
}
}
while(true)
{
for (int i = 1; i <= n; ++ i)
{
pre[i] = i;
for (int j = 1; j <= n; ++ j)
{
if(d[pre[i]][i] > d[j][i]) pre[i] = j;
}
}
for (int i = 1; i <= n; ++ i) dfn[i] = low[i] = vis[i] = scc[i] = 0;
tot = scc_cnt = 0;
for (int i = 1; i <= n; ++ i) if(!dfn[i]) tarjan(i);
// for (int i = 1; i <= n; ++ i) printf("%d ", scc[i]);
// printf("\n\n");
if(scc_cnt == n)
{
for (int i = 2; i <= n; ++ i) res += d[pre[i]][i];
break;
}
for (int i = 2; i <= n; ++ i)
{
if(scc[pre[i]] == scc[i]) res += d[pre[i]][i];
}
for (int i = 1; i <= scc_cnt; ++ i)
{
for (int j = 1; j <= scc_cnt; ++ j) bd[i][j] = INF;
}
for (int i = 1; i <= n; ++ i)
{
for (int j = 1; j <= n; ++ j)
{
if(d[i][j] < INF && scc[i] != scc[j])
{
int a = scc[i], b = scc[j];
if(scc[pre[j]] == scc[j]) bd[a][b] = min(bd[a][b], d[i][j] - d[pre[j]][j]);
else bd[a][b] = min(bd[a][b], d[i][j]);
}
}
}
n = scc_cnt;
for (int i = 1; i <= scc_cnt; ++ i)
{
for (int j = 1; j <= scc_cnt; ++ j) d[i][j] = bd[i][j];
}
}
return res;
}
int main()
{
while(scanf("%d", &n) != EOF)
{
scanf("%d", &m);
for (int i = 1; i <= n; ++ i) scanf("%lf %lf", &p[i].x, &p[i].y);
for (int i = 1; i <= n; ++ i)
{
for (int j = 1; j <= n; ++ j) g[i][j] = 0;//清空aaa
}
for (int i = 1; i <= m; ++ i)
{
int a, b; scanf("%d %d", &a, &b);
if(b != 1 && a != b) g[a][b] = true;
}
if(!check()) printf("poor snoopy\n");
else printf("%.2lf\n", ZL());
}
return 0;
}

浙公网安备 33010602011771号