思路:唯一一个值得一提的就是建一个0号根节点,往每个房子建一条边,权值为房子的高度乘以X。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define Maxn 1010 #define Maxm Maxn*Maxn #define inf 100000000 using namespace std; int head[Maxn],vi[Maxn],id[Maxn],in[Maxn],pre[Maxn],cnt,X,Y,Z,n,e; struct Edge{ int u,v,next,val; }edge[Maxm]; struct Point{ int x,y,z; }p[Maxn]; void init() { memset(head,-1,sizeof(head)); e=0; } void add(int u,int v,int val) { edge[e].u=u,edge[e].v=v,edge[e].val=val,edge[e].next=head[u],head[u]=e++; } int Dis(Point a,Point b) { return abs(a.x-b.x)+abs(a.y-b.y)+abs(a.z-b.z); } int directed_MST(int root,int N,int E) { int ans=0,i,j,u,v; while(1) { for(i=0;i<N;i++) in[i]=inf; for(i=0;i<E;i++) { u=edge[i].u,v=edge[i].v; if(in[v]>edge[i].val&&u!=v) { pre[v]=u; in[v]=edge[i].val; } } for(i=0;i<N;i++) { if(i==root) continue; if(in[i]==inf) return -1; } memset(vi,-1,sizeof(vi)); memset(id,-1,sizeof(id)); cnt=0; in[root]=0; for(i=0;i<N;i++)//枚举每个可能是环上的点 { ans+=in[i]; v=i; while(vi[v]!=i&&id[v]==-1&&v!=root) { vi[v]=i;v=pre[v]; } if(v!=root&&id[v]==-1)//如果退出上个循环的条件不是v==root,就是vi[i]==i,即找到了环 { for(u=pre[v];u!=v;u=pre[u])//对环进行统一标号 id[u]=cnt; id[v]=cnt++; } } if(cnt==0) break;//无环 ,退出 for(i=0;i<N;i++) if(id[i]==-1) id[i]=cnt++; for(i=0;i<E;i++)//进行缩点,并修改每条边的权值。 { v=edge[i].v; edge[i].u=id[edge[i].u]; edge[i].v=id[edge[i].v]; if(edge[i].u!=edge[i].v) edge[i].val-=in[v]; } N=cnt; root=id[root]; } return ans; } int main() { int i,j,a,b,c,k; while(scanf("%d%d%d%d",&n,&X,&Y,&Z),n|X|Y|Z) { init(); for(i=1;i<=n;i++) scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].z); for(i=1;i<=n;i++) { scanf("%d",&k); for(j=1;j<=k;j++) { scanf("%d",&a); if(a==i) continue; if(p[a].z>p[i].z) add(i,a,Dis(p[a],p[i])*Y+Z); else add(i,a,Dis(p[a],p[i])*Y); } } for(i=1;i<=n;i++) add(0,i,X*p[i].z); printf("%d ",directed_MST(0,n+1,e)); } return 0; }