hdu(4009)最小树形图自己建立root

时间:2022-01-23 06:39:20

题意:有n个地方需要供水,每个地方都可以选择是自己挖井,还是从别的地方引水,根据方法不同和每个地方的坐标不同,花费也不同,现在给出每个地方的坐标,花费的计算方法,以及每个地方可以给哪些地方供水(即对方可以从这里引水),求给所有地方供水的最小花费。

思路:建立一个源点,到每个点的距离为自己打井的费用,其他的按条件建边。根据poj3164改过来的。

#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <cmath>
#define INF 2000000000
#define MAXN 1005
#define MAXM 1005
#define eps 1e-4
using namespace std;
typedef int type;
struct Point
{
int x,y,z;
}p[MAXN*MAXN];
int X,Y,Z;
struct node
{
int u, v;
type w;
}edge[MAXN * MAXN];
int E;
int pre[MAXN], id[MAXN], vis[MAXN], n, m;
type in[MAXN];
int dis(Point a, Point b)
{
return abs(a.x-b.x)+abs(a.y-b.y)+abs(a.z-b.z);
}
type Directed_MST(int root, int V, int E)
{
type ret = 0;
while(true)
{
//1.找最小入边
for(int i = 0; i < V; i++)
in[i] = INF;
for(int i = 0; i < E; i++)
{
int u = edge[i].u;
int v = edge[i].v;
if(edge[i].w < in[v] && u != v)
{pre[v] = u; in[v] = edge[i].w;}
}
for(int i = 0; i < V; i++)
{
if(i == root) continue;
if(in[i] == INF) return -1;//除了根以外有点没有入边,则根无法到达它
}
//2.找环
int cnt = 0;
memset(id, -1, sizeof(id));
memset(vis, -1, sizeof(vis));
in[root] = 0;
for(int i = 0; i < V; i++) //标记每个环
{
ret += in[i];
int 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] = cnt;
id[v] = cnt++;
}
}
if(cnt == 0) break; //无环 则break
for(int i = 0; i < V; i++)
if(id[i] == -1) id[i] = cnt++;
//3.建立新图
for(int i = 0; i < E; i++)
{
int u = edge[i].u;
int v = edge[i].v;
edge[i].u = id[u];
edge[i].v = id[v];
if(id[u] != id[v]) edge[i].w -= in[v];
}
V = cnt;
root = id[root];
}
return ret;
}

int main()
{
while(scanf("%d%d%d%d", &n,&X,&Y,&Z)!= EOF)
{
if(!n&&!X&&!Y&&!Z)break;
E=0;
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].z);
edge[E].u=0;
edge[E].v=i;
edge[E].w=p[i].z*X;
E++;
}
for(int i=1;i<=n;i++)
{
int k,rel;
scanf("%d",&k);
while(k--)
{
scanf("%d",&rel);
if(rel==i)
{
edge[E].u=i;edge[E].v=rel;
edge[E].w=INF;
}
else
{
edge[E].u=i;edge[E].v=rel;
if(p[i].z>=p[rel].z)
edge[E].w=dis(p[i],p[rel])*Y;
else
edge[E].w=dis(p[i],p[rel])*Y+Z;
E++;
}
}
}
int ans=Directed_MST(0,n+1,E);
cout<<ans<<endl;
}
return 0;
}