[POJ3164]Command Network
题目
题解
最小树形图的模板题,主要是存一发朱刘算法的模板.
代码
#include<cstdio>
#include<queue>
#include<cstring>
#include<cmath>
using namespace std;
namespace IO{
#define rep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i<=i##_end_;++i)
#define fep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i>=i##_end_;--i)
#define erep(i,u) for(signed i=tail[u],v=e[i].to;i;i=e[i].nxt,v=e[i].to)
#define writc(a,b) fwrit(a),putchar(b)
#define mp(a,b) make_pair(a,b)
#define fi first
#define se second
typedef long long LL;
// typedef pair<int,int> pii;
typedef unsigned long long ull;
typedef unsigned uint;
#define Endl putchar('\n')
// #define int long long
// #define int unsigned
// #define int unsigned long long
#define cg (c=getchar())
template<class T>inline void read(T& x){
char c;bool f=0;
while(cg<'0'||'9'<c)f|=(c=='-');
for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
if(f)x=-x;
}
template<class T>inline T read(const T sample){
T x=0;char c;bool f=0;
while(cg<'0'||'9'<c)f|=(c=='-');
for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
return f?-x:x;
}
template<class T>void fwrit(const T x){//just short,int and long long
if(x<0)return (void)(putchar('-'),fwrit(-x));
if(x>9)fwrit(x/10);
putchar(x%10^48);
}
template<class T>inline T Max(const T x,const T y){return x<y?y:x;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
inline int gcd(const int a,const int b){return b?gcd(b,a%b):a;}
inline void getInv(int inv[],const int lim,const int MOD){
inv[0]=inv[1]=1;for(int i=2;i<=lim;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
}
inline LL mulMod(const LL a,const LL b,const LL mod){//long long multiplie_mod
return ((a*b-(LL)((long double)a/mod*b+1e-8)*mod)%mod+mod)%mod;
}
}
using namespace IO;
const int maxn=100;
const int inf=(1<<30)-1;;
class Directed_MST{
private:
/** @brief 图的信息*/
double w[maxn+5][maxn+5];
/** @brief 是否处在当前的环中*/
bool incir[maxn+5];
/** @brief 是否被收缩*/
bool shrink[maxn+5];
/** @brief 最小前驱边的指向*/
int pre[maxn+5];
/** @brief 节点个数*/
int n;
/** @brief 统计从 @p s 开始能到达多少个节点*/
inline int bfs(const int s){
int ret=1;
bool vis[maxn+5]={};
queue<int>q;
q.push(s);vis[s]=true;
while(!q.empty()){
int u=q.front();q.pop();
rep(v,1,n)if(w[u][v]<inf && !vis[v]){
++ret,vis[v]=true;
q.push(v);
}
}return ret;
}
public:
inline void Init(const int N){
n=N;
memset(incir,0,sizeof incir);
memset(shrink,0,sizeof shrink);
rep(i,0,n)rep(j,i,n)
w[i][j]=w[j][i]=inf;
}
inline void insert(const int u,const int v,double val){
if(w[u][v]>val)w[u][v]=val;
}
inline double launch(const int s){
double ans=0;
if(bfs(s)!=n)return -1;
while(1){//重复执行下列操作
rep(i,1,n)if(i!=s && !shrink[i]){
w[i][i]=inf,pre[i]=i;
rep(j,1,n)if(!shrink[j] && w[j][i]<w[pre[i]][i])
pre[i]=j;
}
/** 找环, 以及环上的任意一个点*/
int u;
for(u=1;u<=n;++u)if(u!=s && !shrink[u]){
int j=u,cnt=0;
while(j!=s && pre[j]!=u &&cnt<=n)j=pre[j],++cnt;
if(j==s || cnt>n)continue;
break;
}
/** 没有环, 可以返回答案了*/
if(u>n){
rep(i,1,n)if(i!=s && !shrink[i])ans+=w[pre[i]][i];
return ans;
}
/** 把环上的所有点全部缩到 u 上*/
int p=u;
memset(incir,0,sizeof incir);
do{
ans+=w[pre[p]][p],p=pre[p];
incir[p]=shrink[p]=1;
}while(p^u);
shrink[u]=0;//这个点肯定没被缩进去
rep(i,1,n)if(incir[i]){//枚举环上的点
rep(j,1,n)if(!incir[j]){//非环上的点
if(w[u][j]>w[i][j])w[u][j]=w[i][j];//连出去的都是最优边
if(w[j][i]<inf && w[j][i]-w[pre[i]][i]<w[j][u])//更新入边
w[j][u]=w[j][i]-w[pre[i]][i];
}
}
}return ans;
}
}G;
int n,m;
double x[maxn+5],y[maxn+5];
inline double dis(const int i,const int j){
return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}
signed main(){
while(~scanf("%d%d",&n,&m)){
rep(i,1,n)scanf("%lf%lf",&x[i],&y[i]);
G.Init(n);
int u,v;
rep(i,1,m){
scanf("%d%d",&u,&v);
if(u==v)continue;
G.insert(u,v,dis(u,v));
}double ans=G.launch(1);
if(ans<0)puts("poor snoopy");
else printf("%.2f\n",ans);
}
return 0;
}