Transfer water HDU - 4009

//将自己凿井那部分构造成以0为起点,凿井地为终点,所花费为边权值,然后,找以0为根(因为肯定会有自凿井)最小树形图 

//如果没有一个自打井的费用比连管低的话,无根最小树形图
//

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
using namespace std;

#define ms(x, n) memset(x,n,sizeof(x));
typedef  long long LL;
const int inf = 0x3f3f3f3f;
const LL maxn = 1005;
int pre[maxn], id[maxn], vis[maxn], in[maxn];
int pos;
int N, X, Y, Z, w[maxn][maxn];

struct Edge {
	int u, v, w;
	Edge(int uu, int vv, int ww) {
		u = uu, v = vv, w = ww;
	}
	Edge() {}
} es[maxn*maxn];
struct Point {
	int x, y, z;
} p[maxn];

int getE(Point a,Point b) {
	int c = abs(a.x-b.x)+abs(a.y-b.y)+abs(a.z-b.z);

	c *= Y;
	if(a.z < b.z)
		c += Z;
	return c;
}
int zhuliu(int root, int n, int m) {
	int res = 0, u, v;
	
	while(true) {

		//      fill(in, in+n, inf);

		for(int i=0; i<=n; i++) {
			in[i] = inf;
		}
		for(int i = 0; i < m; ++i)
			if(es[i].u != es[i].v && es[i].w < in[es[i].v]) {
				pre[es[i].v] = es[i].u;//最小边的另一端点
				in[es[i].v] = es[i].w;//每一点最小边
			}
		for(int i = 0; i < n; ++i)//不连通
			if(i!=root && in[i]==inf)
				return -1;

		int tn = 0;
		ms(id, -1);
		ms(vis, -1);

		in[root] = 0;

		for(int i = 0; i < n; ++i) {
			res += in[i];

			v = i;

			while(vis[v]!=i && id[v]==-1 && v!=root) {
				vis[v] = i;
				v = pre[v];
			}//检查是否有自回环
			if(v!=root && id[v]==-1) { //当有自回环的时候,修改环为点 (缩环)
				for(int u = pre[v]; u != v; u = pre[u])
					id[u] = tn;
				id[v] = tn++;
			}
		}
		if(tn == 0) break;//说明没有一个自回环,得结果结束

		for(int i = 0; i < n; ++i)
			if(id[i]==-1)//不在自回环里的点号更新
				id[i] = tn++;

		//建立新图继续操作
		for(int i = 0; i < m; i++) { //将边修改为点的标号,即去掉自回环
			v = es[i].v;
			es[i].u = id[es[i].u];
			es[i].v = id[es[i].v];
			if(es[i].u != es[i].v)//不在自回环里面
				es[i].w -= in[v];//若指向自回环,那么它的边权就要减少in[v]等价于整个环的边权减去in[v]
		}
		//而如果没有指向有向环,说明它与这个有向环毫无关系,那么在之前的寻找自环缩点过
		//程中已经把这条边的权值加上了,所以这里避免重复计算让这条边的权值减小in[v]会变为0

		n = tn;//点数
		root = id[root];//此时根结点点号
	}
	return res;
}

int main() {
	int T, a, b, c;

	while(~scanf("%d%d%d%d", &N, &X, &Y, &Z)) {
		if(N == 0 && X == 0 && Y == 0 && Z ==0) {
			return 0;
		}
		
		int L = 0;
		
		for(int i=1; i<=N; i++)
			scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].z);
		
		for(int i=1; i<=N; i++) {
			int k, v;
		
			scanf("%d", &k);
		
			for(int j=1; j<=k; j++) {
		
				scanf("%d", &v);
		
				es[L].u=i;
				es[L].v=v;
				es[L++].w=getE(p[i], p[v]);
			}
		}
		//自己凿井
		for(int i=1; i<=N; i++) {
			es[L].u=0;
			es[L].v=i;
			es[L++].w=p[i].z*X;
		}

		int ans = zhuliu(0, N+1, L);

		if(ans == -1) printf("poor XiaoA\n");
		else printf("%d\n", ans);
	}

	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值