朱刘算法
干什么的
朱刘算法就是最小生成树算法在有向图上的应用。
大概说说
给定一个有向图,有向图定义一个根节点,求一个最小树形图。
树形图就是一颗有向树。
而在这个算法中我们要给定几个条件:
-
没有有向环
-
每个点的入度为1(除了根节点之外)
放在题目中看
先贪心的从每个点的所有入边中找一条权值最小的边,从选出的边中判断是否存在环;
如果不存在环,结束,把所有边权值加上作为答案;如果存在环,进入下一步。将所有环缩点,构造新图,缩点前把所有边权值加上。每次缩一次点,点数最少减一,所以总共最多迭代 \(n\) 次算法结束。
#include<bits/stdc++.h>
#define rint register int
#define int long long
#define endl '\n'
#define x first
#define y second
using namespace std;
const int N = 1e2;
int n,m;
pair<double,double> q[N];
bool g[N][N],st[N],ins[N];
double d[N][N], bd[N][N];
int pre[N],bpre[N],dfn[N],low[N],ts,stk[N],top,id[N],cnt;
void inline dfs(int u){
st[u]=1;
for(rint i=1;i<=n;i++){
if(g[u][i]&&!st[i]){
dfs(i);
}
}
return ;
}
bool inline check_con(){
memset(st,0,sizeof st);
dfs(1);
for(rint i=1;i<=n;i++){
if(!st[i]){
return 0;
}
}
return 1;
}
double inline get_dist(int a,int b){
double dx=q[a].x-q[b].x,dy=q[a].y-q[b].y;
return sqrt(dx*dx+dy*dy);
}
void inline tarjan(int u){
dfn[u]=low[u]=++ts;
stk[++top]=u;
ins[u]=1;
int j=pre[u];
if(!dfn[j]){
tarjan(j);
low[u]=min(low[u],low[j]);
}else if(ins[j]){
low[u]=min(low[u], dfn[j]);
}if(low[u]==dfn[u]){
int y;cnt++;
do{
y=stk[top--];
ins[y]=0;
id[y]=cnt;
}while(y!=u);
}
return ;
}
double inline work(){
double res=0;
for(rint i=1;i<=n;i++){
for(rint j=1;j<=n;j++){
if(g[i][j]){
d[i][j]=get_dist(i,j);
}else{
d[i][j]=0x3f3f3f3f;
}
}
}
while(true){
for(rint i=1;i<=n;i++){
pre[i]=i;
for(rint j=1;j<=n;j++){
if(d[pre[i]][i]>d[j][i]){
pre[i]=j;
}
}
}
memset(dfn,0,sizeof dfn);
ts=cnt=0;
for(rint i=1;i<=n;i++){
if(!dfn[i]){
tarjan(i);
}
}
if(cnt==n){
for(rint i=2;i<=n;i++)
res+=d[pre[i]][i];
break;
}
for(rint i=2;i<=n;i++){
if(id[pre[i]]==id[i]){
res+=d[pre[i]][i];
}
}
for(rint i=1;i<=cnt;i++)
for(rint j=1;j<=cnt;j++)
bd[i][j]=0x3f3f3f3f;
for(rint i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if(d[i][j]<0x3f3f3f3f&&id[i]!=id[j]){
int a=id[i];
int b=id[j];
if(id[pre[j]]==id[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=cnt;
memcpy(d,bd,sizeof d);
}
return res;
}
signed main(){
while(~scanf("%lld%lld",&n,&m)){
for(rint i=1;i<=n;i++)
cin>>q[i].x>>q[i].y;
memset(g,0,sizeof g);
while(m--){
int a,b;
cin>>a>>b;
if(a!=b&&b!=1)
g[a][b]=1;
}
if(!check_con()){
puts("poor snoopy");
}else{
printf("%.2lf\n", work());
}
}
return 0;
}