题目链接:点击打开链接
题意描述:在一个村庄有n户人家(x,y,h),现在打算在村庄中挖井和建水渠使每户人家都可以用上水
1、如果挖井费用为高度h*X;
2、如果a->b建水渠,if(a.h>=b.h)费用为两点距离dis*Y,反之费用为dis*Y+Z
问,怎样挖井和建水渠才能使费用最小?
解体思路:最小树形图:点击打开链接
分析:我们可以新建一个源点指向所有的用户,权值为挖井的费用,然后根据是否能建水渠构建个用户之间的关系,求图的最小树形图即可
代码:
#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#define MAXN 1010
#define INF 0x7fffffff
using namespace std;
struct Point{
int x,y,z;
}p[MAXN];
struct Edge{
int u,v,cost;
Edge(){}
Edge(int u,int v,int cost):u(u),v(v),cost(cost){}
}edge[MAXN*MAXN];
int n,m,X,Y,Z;
inline int get_cost(Point a,Point b){
int dis=abs(a.x-b.x)+abs(a.y-b.y)+abs(a.z-b.z);
if(a.z>=b.z) return dis*Y;
return dis*Y+Z;
}
int pre[MAXN];///记录当前顶点的前驱顶点
int id[MAXN];///经过缩点之后:原图顶点号->新图顶点号
int in[MAXN];///记录顶点所有入边的权值的最小值
int vis[MAXN];///是否访问过当前顶点
inline int ZhuLiu(int root,int n,int m){
int u,v;
int ret=0;///最小树形图的权值
while(true){
for(int i=0;i<=n;++i) in[i]=INF;
/*记录顶点所有入边的权值的最小值*/
for(int i=0;i<m;++i){
u=edge[i].u; v=edge[i].v;
if(edge[i].cost<in[v]&&u!=v){///去除v->v这种入边
pre[v]=u;
in[v]=edge[i].cost;
}
}
/*除根节点外其他顶点每个点都必须有入边,如果没有则无法构成树,返回INF表示不存在*/
for(int i=0;i<=n;++i){
if(i==root) continue;
if(in[i]==INF) return INF;
}
in[root]=0;
int cnt=0;///对每个顶点进行重新编号
memset(id,-1,sizeof(id));
memset(vis,-1,sizeof(vis));
for(int i=0;i<=n;++i){
ret+=in[i];
v=i;
/*构造环*/
while(vis[v]!=i&&id[v]==-1&&v!=root){
vis[v]=i;
v=pre[v];
}
/*判断是否存在环,如果存在环则进行缩点,环中每一个顶点对应同一个顶点cnt*/
if(v!=root&&id[v]==-1){
for(u=pre[v];u!=v;u=pre[u]) id[u]=cnt;
id[v]=cnt++;
}
}
/*如果不存在环,则当前树即为最小树形图*/
if(cnt==0) break;
/*对于其他不在环中的顶点进行编号*/
for(int i=0;i<=n;++i){
if(id[i]==-1) id[i]=cnt++;
}
/*对相应的边进行修改*/
for(int i=0;i<m;++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].cost-=in[v];
}
n=cnt-1;
root=id[root];
}
return ret;
}
int main(){
while(scanf("%d%d%d%d",&n,&X,&Y,&Z)!=EOF){
if(n==0&&X==0&&Y==0&&Z==0) break;
for(int i=1;i<=n;++i) scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].z);
int k,to;m=0;
for(int i=1;i<=n;++i){
scanf("%d",&k);
while(k--){
scanf("%d",&to);
edge[m++]=Edge(i,to,get_cost(p[i],p[to]));
}
}
for(int i=1;i<=n;++i)
edge[m++]=Edge(0,i,p[i].z*X);
printf("%d\n",ZhuLiu(0,n,m));
}
return 0;
}