题目链接:点击打开链接
#include
#include
#include
#include
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 1005;
int N,X,Y,Z;
int closest[maxn],in[maxn],vis[maxn],id[maxn];
struct Point{///存居民家的三维坐标
int x,y,z;
}point[maxn];
struct Edge
{
int s;
int t;
int cost;
}edge[maxn*maxn];
int Distance(struct Point a,struct Point b)///计算曼哈顿距离
{
return fabs(a.x-b.x)+fabs(a.y-b.y)+fabs(a.z-b.z);
}
int Min_Cost(int root,int nodenum,int edgenum)
{
int ans = 0;
int i;
while(true)
{
for(i = 0; i < nodenum; i++)
in[i] = inf;///先将所有节点的入边权值都刷为无穷大
for(i = 0; i < edgenum; i++)
{
int s = edge[i].s;
int t = edge[i].t; ///s->t这条边
if(edge[i].cost < in[t] && s!=t)
{
closest[t] = s;///记录与t最近的点,也是t点的入边的始点
in[t] = edge[i].cost;///记录t的最小入边权值
}
}///找某个节点的入边最小权值
int cnt = 0;
memset(vis,-1,sizeof(vis));
memset(id,-1,sizeof(id));
in[root] = 0;///将根节点的入边权值刷为0,防止会出现图中所有的点一起连成一个大环
for(i = 0; i < nodenum; i++)
{
ans += in[i];///第一遍先将所有点的入边权值都加上
int t = i;
while(vis[t]!=i && id[t]==-1 && t != root)
/**如果该点未被标记,并且没有新的编号并且该点不是根节点,就一直找它相邻最近的点,
一层一层的找,如果有环肯定会找到自身,因为之前第一次经过自己的时候已经被标记了,
再次回到自身就不满足条件,就要跳出了。如果没有环,一定会找到根节点,遇见根节点也会跳出**/
{
vis[t] = i;
t = closest[t];
}
if(t != root && id[t] == -1)
/**如果该点是根节点就说明该图没有环,如果不是根节点,就说明该图是有环的,
并且该环没有被赋予新的编号**/
{
for(int s = closest[t]; s != t; s = closest[s])
{
id[s] = cnt;///将该环中的所有点都重新赋予新的编号,并且编号都一样,将它们看做一个超大点
}
id[t] = cnt++;
}
}
if(cnt == 0)
break;///如果此时的cut任然为0,说明该图已经没有环,那么ans就是最小树形图的值了
for(i = 0; i < nodenum; i++)///如果没有跳出就代表该图还是有环的,就要重新将本图的点赋予新的 编号
if(id[i] == -1)
id[i] = cnt++;
for(i = 0; i < edgenum; i++)
{
int s = edge[i].s;
int t = edge[i].t;
edge[i].s = id[s];
edge[i].t = id[t];
if(s != t)
/**删边,这句代码真的很难理解,但我还是想尽自己的理解给大家解释一番,
就是如果有环的话那么我们肯定多算了一条边,就要在环里的点的入边权值上扣掉这些,
如果两个点都不是环里的点,减去最小入边权值最小为0,而我们在下一次寻找最小入边权值的话就会找到这个0,
加在ans上相当于没有加,是因为第一次已经加上了。**/
edge[i].cost = edge[i].cost - in[t];
}
nodenum = cnt;///更新新的节点个数
root = id[root];///更新新的根节点
}
return ans;
}
int main()
{
while(~scanf("%d %d %d %d",&N,&X,&Y,&Z)&&(N+X+Y+Z))
{
int k=0;
for(int i=1;i<=N;i++)
{
scanf("%d %d %d",&point[i].x,&point[i].y,&point[i].z);
}
for(int i=1;i<=N;i++)///存将虚拟根节点到各个节点的权值刷为该户打井需要的费用
{
edge[k].s = 0;
edge[k].t= i;
edge[k].cost = X*point[i].z;
k++;
}
for(int i = 1; i <= N; i++)
{
int t,node;
scanf("%d",&t);
for(int j=1;j<=t;j++)
{
scanf("%d",&node);
edge[k].s = i;
edge[k].t = node;
edge[k].cost = Distance(point[i],point[node])*Y;
if(point[i].z