本题题意:给你一堆点的坐标n个坐标,然后m对点表示a点到b点有单向边。然后指定第一个点,问第一个点能不能通过已经给出的边连接到所有点。如果不能输出可怜的某狗狗的名字,如果可以输出最短的路径和。
思路:在生成树里看到的这个专题,想了一下因为要单点连接到所有点,不是最小生成树也不是次小生成树。百度了一下题解,又发现了最小树形图这个神奇的东西。然后学到了朱刘算法。算法我就不说了(怕说不清坑人),思路就直接给出大佬的博客最小树形图-朱刘算法。下面附了本题代码(因为大佬博客里的例题不是这个),代码注释应该能帮助刚学这个算法的人理解。。。
题目链接
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
#include<queue>
#include<cmath>
#include<set>
#include<list>
using namespace std;
typedef long long ll;
typedef double db;
const int inf=0x3f3f3f;
const int maxn=210,maxm=50010;
int pre[maxn],vis[maxn],idd[maxn],n,m,pos,sz,x[maxn],y[maxn];
db inn[maxn];
struct node{
int u,v;
db l;
}ed[maxm];
db dit(int a,int b)
{
return sqrt((x[a]-x[b])*(x[a]-x[b])*1.0+(y[a]-y[b])*(y[a]-y[b]));
}
void add(int a,int b)
{
sz++;
ed[sz].u=a;
ed[sz].v=b;
ed[sz].l=dit(a,b);
}
db zhuliu(int rt)
{
db ans=0;
while(true)
{
for(int i=1;i<=n;i++)inn[i]=inf;//初始化
for(int i=1;i<=sz;i++)//开始找最小入边
{
if(ed[i].u==ed[i].v)continue;
else if(ed[i].l<inn[ed[i].v])
{
pre[ed[i].v]=ed[i].u;
inn[ed[i].v]=ed[i].l;
}
}
for(int i=1;i<=n;i++)//如果有点没有最小入边,证明无法建树返回。
{
if(i!=rt && inn[i]==inf)return -1;
}
int cnt=0;
memset(idd,0,sizeof(idd));
memset(vis,0,sizeof(vis));
inn[rt]=0;
for(int i=1;i<=n;i++)//找环并标号
{
ans+=inn[i];
int x=i;
while(!idd[x] && vis[x]!=i && x!=rt)//找环并标记 1.前驱结点在环上 2. 前驱结点已经在本轮搜索中被搜到 ,证明已经成环
{ // 3.前驱结点是根节点,不能成环 。
vis[x]=i; //给本轮搜索标号,要是搜到同号证明可成环
x=pre[x];
}
if(x!=rt && !idd[x]) //如果前驱结点不是根结点 并且前驱结点不在环上
{
idd[x]= ++cnt;
for(int j=pre[x];j!=x;j=pre[j])idd[j]=cnt;
}
}
if(!cnt)break;//如果没有环证明已经生成最小树形图,退出循环
for(int i=1;i<=n;i++)
{
if(!idd[i])idd[i] = ++cnt;//给单独的没有在环上的点编号。
}
for(int i=1;i<=m;i++)//进行缩点操作
{
int x=ed[i].u,y=ed[i].v;
ed[i].u=idd[x];//给每条边的两个结点更新为缩点后点的编号
ed[i].v=idd[y];
if(idd[x]!=idd[y])ed[i].l-=inn[y];
}
n=cnt;//更新缩点后的结点数量
rt=idd[rt];//更新缩点后根结点的新编号
}
return ans;
}
int main()
{
while(cin>>n>>m)
{
sz=0;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&x[i],&y[i]);
}
for(int i=1;i<=m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
add(a,b);
}
db ans=zhuliu(1);
if(ans==-1)puts("poor snoopy");
else printf("%.2f\n",ans);//只能用f不能双精度输出,原因不详
}
return 0;
}