您的位置:首页 > 其它

POJ 3164 Command Network

2016-02-24 19:20 387 查看
最小树形图的朱刘算法好像是很久之前学的啦

不过早忘光了,今天正好来复习一下。

突然发现思路很清晰嘛(果然是因为找到了一个很好的板子吗)

首先给每个点找最小的入边,如果一个点没有入边,那么可以肯定无解了。(假设有入边但是从根节点到不了,那么必然会在后面缩点,然后就没有入边了)

这时可以将每条入边暂时加入到解中。

然后对于每个点来说找它所有的一个环,然后把环缩成一个点,也就是给环上的每个点打个标号。

如果无环,退出。

将每条边的点用标号重新标记,如果该条边连接两个环,则将边权减去终点的最小入边(假设之后选择这条边,则此时的-in[v]与之前的+in[v]抵消)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int N=100+5;
const int M=10000+5;
const double inf=1000000000.0;
struct Edge{
int u,v;
double w;
}e[M];
double in
;
int pre
,id
,vis
;
double Zhu_Liu(int root,int n,int m){
double ans=0;
while(true){
//step 1 : minimum in edge
for(int i=1;i<=n;i++)in[i]=inf;
for(int i=1;i<=m;i++){
int u=e[i].u,v=e[i].v;
if(u!=v&&in[v]>e[i].w)
in[v]=e[i].w,pre[v]=u;
}
for(int i=1;i<=n;i++)
if(i!=root&&in[i]==inf)return -inf;
//step 2 : find circles
int nv=0;
memset(id,-1,sizeof(id));
memset(vis,-1,sizeof(vis));
in[root]=0;
for(int i=1;i<=n;i++){
ans+=in[i];
int v=i;
while(vis[v]!=i&&id[v]==-1&&v!=root)
vis[v]=i,v=pre[v];
if(v!=root&&id[v]==-1){
id[v]=++nv;
for(int u=pre[v];u!=v;u=pre[u])
id[u]=nv;
}
}
if(!nv)break;
for(int i=1;i<=n;i++)
if(id[i]==-1)id[i]=++nv;
//step 3 : shrink node and re-marking
for(int i=1;i<=m;i++){
int v=e[i].v;
e[i].u=id[e[i].u];
e[i].v=id[e[i].v];
if(e[i].u!=e[i].v)
e[i].w-=in[v];
}
n=nv;
root=id[root];
}
return ans;
}
double x[105],y[105];
double sqr(double x){return x*x;}
double dist(int i,int j){
return sqrt(sqr(x[i]-x[j])+sqr(y[i]-y[j]));
}
int main(){
//freopen("a.in","r",stdin);
int n,m;
while(scanf("%d%d",&n,&m)==2){
for(int i=1;i<=n;i++)
scanf("%lf%lf",&x[i],&y[i]);
for(int i=1;i<=m;i++){
scanf("%d%d",&e[i].u,&e[i].v);
if(e[i].u==e[i].v)i--,m--;
else e[i].w=dist(e[i].u,e[i].v);
}
double ans=Zhu_Liu(1,n,m);
if(ans<0)puts("poor snoopy");
else printf("%.2f\n",ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: