pku 3164 最小树形图
这题本质是求最小树形图。
在所有操作开始之前,我们需要把图中所有的自环全都清除。很明显,自环是不可能在任何一个树形图上的。只有进行了这步操作,总算法复杂度才真正能保证是O(VE)。
首先为除根之外的每个点选定一条入边,这条入边一定要是所有入边中最小的。现在所有的最小入边都选择出来了,如果这个入边集不存在有向环的话,我们可以证明这个集合就是该图的最小树形图。这个证明并不是很难。如果存在有向环的话,我们就要将这个有向环所称一个人工顶点,同时改变图中边的权。假设某点u在该环上,并设这个环中指向u的边权是in[u],那么对于每条从u出发的边(u, i, w),在新图中连接(new, i, w)的边,其中new为新加的人工顶点; 对于每条进入u的边(i, u, w),在新图中建立边(i, new, w-in[u])的边。为什么入边的权要减去in[u],这个后面会解释,在这里先给出算法的步骤。然后可以证明,新图中最小树形图的权加上旧图中被收缩的那个环的权和,就是原图中最小树形图的权。
上面结论也不做证明了。现在依据上面的结论,说明一下为什么出边的权不变,入边的权要减去in [u]。对于新图中的最小树形图T,设指向人工节点的边为e。将人工节点展开以后,e指向了一个环。假设原先e是指向u的,这个时候我们将环上指向u的边 in[u]删除,这样就得到了原图中的一个树形图。我们会发现,如果新图中e的权w'(e)是原图中e的权w(e)减去in[u]权的话,那么在我们删除掉in[u],并且将e恢复为原图状态的时候,这个树形图的权仍然是新图树形图的权加环的权,而这个权值正是最小树形图的权值。所以在展开节点之后,我们得到的仍然是最小树形图。逐步展开所有的人工节点,就会得到初始图的最小树形图了。
code:
在所有操作开始之前,我们需要把图中所有的自环全都清除。很明显,自环是不可能在任何一个树形图上的。只有进行了这步操作,总算法复杂度才真正能保证是O(VE)。
首先为除根之外的每个点选定一条入边,这条入边一定要是所有入边中最小的。现在所有的最小入边都选择出来了,如果这个入边集不存在有向环的话,我们可以证明这个集合就是该图的最小树形图。这个证明并不是很难。如果存在有向环的话,我们就要将这个有向环所称一个人工顶点,同时改变图中边的权。假设某点u在该环上,并设这个环中指向u的边权是in[u],那么对于每条从u出发的边(u, i, w),在新图中连接(new, i, w)的边,其中new为新加的人工顶点; 对于每条进入u的边(i, u, w),在新图中建立边(i, new, w-in[u])的边。为什么入边的权要减去in[u],这个后面会解释,在这里先给出算法的步骤。然后可以证明,新图中最小树形图的权加上旧图中被收缩的那个环的权和,就是原图中最小树形图的权。
上面结论也不做证明了。现在依据上面的结论,说明一下为什么出边的权不变,入边的权要减去in [u]。对于新图中的最小树形图T,设指向人工节点的边为e。将人工节点展开以后,e指向了一个环。假设原先e是指向u的,这个时候我们将环上指向u的边 in[u]删除,这样就得到了原图中的一个树形图。我们会发现,如果新图中e的权w'(e)是原图中e的权w(e)减去in[u]权的话,那么在我们删除掉in[u],并且将e恢复为原图状态的时候,这个树形图的权仍然是新图树形图的权加环的权,而这个权值正是最小树形图的权值。所以在展开节点之后,我们得到的仍然是最小树形图。逐步展开所有的人工节点,就会得到初始图的最小树形图了。
code:
//最小树形图,源点编号为1
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
struct Point {
double _x,_y;
Point(double x,double y) :_x(x),_y(y) {}
Point() {}
};
const unsigned int maxn=128;
const double NOEDGE=99999999;
double G[maxn][maxn];
Point allp[maxn];
int N,M;
double res;
inline double dist(const Point& p1,const Point& p2) {
return sqrt((p1._x-p2._x)*(p1._x-p2._x)+(p1._y-p2._y)*(p1._y-p2._y));
}
template <class T>
void update(T& o,const T& x){
if(o>x)
o=x;
}
bool vis[maxn];
void dfs(int v){
vis[v]=true;
for(int i=2;i<=N;++i)
if((!vis[i])&&G[v][i]!=NOEDGE)
dfs(i);
}
bool possible(){
memset(vis,0,sizeof(vis));
dfs(1);
for(int i=2;i<=N;++i)
if(!vis[i])
return false;
return true;
}
int pre[maxn];
bool del[maxn];
void solve(){
int num=N;
memset(del,0,sizeof(del));
for(;;){
int i;
for(i=2;i<=N;++i){
if(del[i])continue;
pre[i]=i;
G[i][i]=NOEDGE;
for(int j=1;j<=N;++j){
if(del[j])continue;
if(G[j][i]<G[pre[i]][i])
pre[i]=j;
}
}
for(i=2;i<=N;++i){
if(del[i])continue;
int j=i;
memset(vis,0,sizeof(vis));
while(!vis[j]&&j!=1){
vis[j]=true;
j=pre[j];
}
if(j==1)continue;
i=j;
res+=G[pre[i]][i];
for(j=pre[i];j!=i;j=pre[j]){
res+=G[pre[j]][j];
del[j]=true;
}
for(j=1;j<=N;++j){
if(del[j])continue;
if(G[j][i]!=NOEDGE)
G[j][i]-=G[pre[i]][i];
}
for(j=pre[i];j!=i;j=pre[j]){
for(int k=1;k<=N;++k){
if(del[k])continue;
update(G[i][k],G[j][k]);
if(G[k][j]!=NOEDGE)
update(G[k][i],G[k][j]-G[pre[j]][j]);
}
}
for(j=pre[i];j!=i;j=pre[j]){
del[j]=true;
}
break;
}
if(i>N){
for(int i=2;i<=N;++i){
if(del[i])continue;
res+=G[pre[i]][i];
}
break;
}
}
}
int main(){
double x,y;
for(;;){
if(scanf("%d%d",&N,&M)==EOF) return 0;
if(N==0)break;
for(int i=0;i<=N;i++)
for(int j=0;j<=N;j++)
G[i][j]=NOEDGE;
for(int i=1;i<=N;i++) {
scanf("%lf %lf",&x,&y);
allp[i]._x=x;
allp[i]._y=y;
}
for(int i=0;i<M;++i){
unsigned int a,b;
scanf("%u%u",&a,&b);
update(G[a][b],dist(allp[a],allp[b]));
}
if(!possible()){
puts("poor snoopy");
}
else{
res=0;
solve();
printf("%.2f\n",res);
}
}
}
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
struct Point {
double _x,_y;
Point(double x,double y) :_x(x),_y(y) {}
Point() {}
};
const unsigned int maxn=128;
const double NOEDGE=99999999;
double G[maxn][maxn];
Point allp[maxn];
int N,M;
double res;
inline double dist(const Point& p1,const Point& p2) {
return sqrt((p1._x-p2._x)*(p1._x-p2._x)+(p1._y-p2._y)*(p1._y-p2._y));
}
template <class T>
void update(T& o,const T& x){
if(o>x)
o=x;
}
bool vis[maxn];
void dfs(int v){
vis[v]=true;
for(int i=2;i<=N;++i)
if((!vis[i])&&G[v][i]!=NOEDGE)
dfs(i);
}
bool possible(){
memset(vis,0,sizeof(vis));
dfs(1);
for(int i=2;i<=N;++i)
if(!vis[i])
return false;
return true;
}
int pre[maxn];
bool del[maxn];
void solve(){
int num=N;
memset(del,0,sizeof(del));
for(;;){
int i;
for(i=2;i<=N;++i){
if(del[i])continue;
pre[i]=i;
G[i][i]=NOEDGE;
for(int j=1;j<=N;++j){
if(del[j])continue;
if(G[j][i]<G[pre[i]][i])
pre[i]=j;
}
}
for(i=2;i<=N;++i){
if(del[i])continue;
int j=i;
memset(vis,0,sizeof(vis));
while(!vis[j]&&j!=1){
vis[j]=true;
j=pre[j];
}
if(j==1)continue;
i=j;
res+=G[pre[i]][i];
for(j=pre[i];j!=i;j=pre[j]){
res+=G[pre[j]][j];
del[j]=true;
}
for(j=1;j<=N;++j){
if(del[j])continue;
if(G[j][i]!=NOEDGE)
G[j][i]-=G[pre[i]][i];
}
for(j=pre[i];j!=i;j=pre[j]){
for(int k=1;k<=N;++k){
if(del[k])continue;
update(G[i][k],G[j][k]);
if(G[k][j]!=NOEDGE)
update(G[k][i],G[k][j]-G[pre[j]][j]);
}
}
for(j=pre[i];j!=i;j=pre[j]){
del[j]=true;
}
break;
}
if(i>N){
for(int i=2;i<=N;++i){
if(del[i])continue;
res+=G[pre[i]][i];
}
break;
}
}
}
int main(){
double x,y;
for(;;){
if(scanf("%d%d",&N,&M)==EOF) return 0;
if(N==0)break;
for(int i=0;i<=N;i++)
for(int j=0;j<=N;j++)
G[i][j]=NOEDGE;
for(int i=1;i<=N;i++) {
scanf("%lf %lf",&x,&y);
allp[i]._x=x;
allp[i]._y=y;
}
for(int i=0;i<M;++i){
unsigned int a,b;
scanf("%u%u",&a,&b);
update(G[a][b],dist(allp[a],allp[b]));
}
if(!possible()){
puts("poor snoopy");
}
else{
res=0;
solve();
printf("%.2f\n",res);
}
}
}