您的位置:首页 > Web前端

hdu 4009 Transfer water 最小树形图

2016-07-03 01:04 399 查看
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4009
题目大意:
在山上有N户人家,每家的坐标为(xi, yi, zi)。每户人家要吃水,要么自己打井,花费为A zi,要么从别人的家引水渠代价为B
两家的曼哈顿距离,如果这家的海拔比供水的低,还要另外再买一个价值为C的水泵。问每家都有水吃的最低花费是多少。

每家都有水的目标一定能达成,大不了每家都打口井…
把所有能连水渠的两家连起来,就得到了一个有向图,答案一定一个森林。建立一个虚节点,向每个点连一条有向边,权值为在第i家挖井的代价,这样就把答案从森林变成了一棵树,求全图的一个最小树形图就是最低的代价。

学习了一下朱刘算法,算法思路还比较简单,实现起来很巧妙。算是一个模板吧。

#include<bits/stdc++.h>
#define N 1100
#define INF 0x3f3f3f3f
#define M 1100000
using namespace std;

int pre
,id
,visit
,in
;
struct node
{
int u,v,cost;
}edge[M];

int zhuliu(int root,int n,int m)
{
int res=0,u,v;
while(1)
{
for(int i=0;i<n;i++) in[i]=INF;
for(int i=0;i<m;i++)
if(edge[i].u!=edge[i].v&&edge[i].cost<in[edge[i].v])
{
pre[edge[i].v]=edge[i].u;
in[edge[i].v]=edge[i].cost;
}
for(int i=0;i<n;i++)
if(i!=root&&in[i]==INF)
return -1;
int tn=0;
memset(id,-1,sizeof(id));
memset(visit,-1,sizeof(visit));
in[root]=0;
for(int i=0;i<n;i++)
{
res+=in[i];
v=i;
while(visit[v]!=i&&id[v]==-1&&v!=root)
{
visit[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;)
{
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];
else
swap(edge[i],edge[--m]);
}
n=tn;
root=id[root];
}
return res;
}

int n,x
,y
,z
,a,b,c,cnt;

void addedge(int u,int v,int cost)
{
edge[cnt].u=u;
edge[cnt].v=v;
edge[cnt++].cost=cost;
}

int main()
{
while(~scanf("%d%d%d%d",&n,&a,&b,&c)&&n)
{
cnt=0;
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&x[i],&y[i],&z[i]);
addedge(0,i,a*z[i]);
}
for(int i=1;i<=n;i++)
{
int t;
scanf("%d",&t);
for(int j=0;j<t;j++)
{
int u;
scanf("%d",&u);
if(u==i) continue ;
int cost=(abs(x[i]-x[u])+abs(y[i]-y[u])+abs(z[i]-z[u]))*b;
if(z[i]<z[u]) addedge(i,u,cost+c);
else addedge(i,u,cost);
}
}
cout<<zhuliu(0,n+1,cnt)<<endl;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: