题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4009
题意:给出一个村庄(x,y,z)。每个村庄可以挖井或者修建水渠从其他村庄得到水。挖井有一个代价,修水渠有一个代价。另外A村庄只能向其指定的一些村庄供水。使得所有村庄有水求最小代价。
思路:增加虚拟点0,向所有点连边表示挖井。能连边的连边。求最小树形图即可。
struct point
{
int x,y,z;
};
struct edge
{
int u,v,w;
};
point p[N];
edge e[N*N];
int eCnt,n,pre[N],id[N],in[N],visit[N];
void add(int u,int v,int w)
{
e[eCnt].u=u;
e[eCnt].v=v;
e[eCnt++].w=w;
}
int directedMST(int root)
{
int ans=0,nv=n,i;
while(1)
{
for(i=0;i<nv;i++) in[i]=INF;
for(i=0;i<eCnt;i++)
{
int u=e[i].u;
int v=e[i].v;
if(u!=v&&e[i].w<in[v])
{
in[v]=e[i].w;
pre[v]=u;
}
}
for(i=0;i<nv;i++)
{
if(i!=root&&inf==in[i]) return -1;
}
int nodeCnt=0;
for(i=0;i<nv;i++) id[i]=visit[i]=-1;
in[root]=0;
for(i=0;i<nv;i++)
{
ans+=in[i];
int v=i;
while(visit[v]!=i&&id[v]==-1&&v!=root)
{
visit[v]=i;
v=pre[v];
}
if(v!=root&&-1==id[v])
{
int u;
for(u=pre[v];u!=v;u=pre[u]) id[u]=nodeCnt;
id[v]=nodeCnt++;
}
}
if(0==nodeCnt) break;
for(i=0;i<nv;i++) if(-1==id[i]) id[i]=nodeCnt++;
for(i=0;i<eCnt;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];
}
nv=nodeCnt;
root=id[root];
}
return ans;
}
int X,Y,Z;
int Dis(point a,point b)
{
int dis=abs(a.x-b.x)+abs(a.y-b.y)+abs(a.z-b.z);
dis*=Y;
if(a.z<b.z) dis+=Z;
return dis;
}
int main()
{
while(scanf("%d%d%d%d",&n,&X,&Y,&Z)!=EOF)
{
if(!n&&!X&&!Y&&!Z) break;
int i,j,u,v;
n++;
for(i=1;i<n;i++) scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].z);
eCnt=0;
for(i=1;i<n;i++)
{
int k;
scanf("%d",&k);
while(k--)
{
int t;
scanf("%d",&t);
if(t==i) continue;
add(i,t,Dis(p[i],p[t]));
}
add(0,i,p[i].z*X);
}
printf("%d
",directedMST(0));
}
return 0;
}