您的位置:首页 > Web前端

HDU 4009 Transfer water【最小树形图】

2017-11-02 19:46 369 查看
题目链接

题目意思

小A住在一个村庄,去年洪水淹没了这个村庄。因此他们决定将整个村庄搬到附近的山上。山上没有泉水,因此每一家必须选择打一个井或者从别的人家引水。如果一家决定去挖一口井,则他们打井的费用是他们房子的高度,X每米。如果决定去引水,如果引水的地方比当前地方高,则引水的钱是这两家的距离成上Y美元每米。如果引水的地方比当前低。则必须需要水泵(除了引水线后)还要花费Z美元买一个水泵。除此之外,村民的关系是需要考虑的。一些村民不允许一些村民从他们的房子引水。现在给出3个实数(a,b,c)。每个房子的坐标。计算整个村庄都通上水的最小花费。如果不能,则输出不能。

解题思路

这是一道最小树形图的题。因为每个点都可以选择打井或者是其他人家里引水,所以打井的位置是不能确定的。

这里就有一个巧妙的办法,我们建立一个人工节点0,将节点0到其他节点的权值赋成在节点打井的费用即可。要注意的是如果是从低处向高处引水,除了引水费用还要加上水泵的钱。

这样建立新图后就相当于求最小树形图。

代码部分

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
int x,y,n,z;
struct node
{
int x;
int y;
int z;
} p[1002];
struct Edge
{
int u;
int v;
long long int w;
} edge[1002*1002];
long long int dis(node a,node b)
{
long long int temp=abs(a.x-b.x)+abs(a.y-b.y)+abs(a.z-b.z);
return temp;
}
///closest[i]表示距离i最近的点,vis标记数组,in[i]存放节点的入边最小权值,id[i]存放节点的编号
int closest[1005],vis[1005],in[1005],id[1005];
int minroot;
long long int min_cost(int root,int nodenum,int edgenum)
{
long long int ans=0;
int i;
while(true)
{
for(i=0; i<nodenum; i++)
in[i]=INF;
for(i=0; i<edgenum; i++)
{
int u=edge[i].u;
int v=edge[i].v;
///u到v的权值如果比当前v的入边最小权还小,就更新最小权值,更新到v的最近的点为u
if(u!=v&&edge[i].w<in[v])
{
in[v]=edge[i].w;
closest[v]=u;
//if(u==root)///某个点的前驱节点是虚拟节点
//  minroot=i;
}
}
for(i=0; i<nodenum; i++)
{
if(i==root)
continue;
if(in[i]==INF)
return -1;
}
int cnt=0;
memset(vis,-1,sizeof(vis));
memset(id,-1,sizeof(id));
in[root]=0;
for(i=0; i<nodenum; i++)
{
ans+=in[i];
int t=i;
///从t开始一直往前找,如果有环则会回到自身
while(vis[t]!=i&&id[t]==-1&&t!=root)
{
vis[t]=i;
t=closest[t];
}
if(t!=root&&id[t]==-1)
{
for(int u=closest[t]; u!=t; u=closest[u])
{
id[u]=cnt;
}
id[t]=cnt++;
}
}
if(cnt==0)
break;
for(i=0; i<nodenum; i++)///为不在环中的节点编号
{
if(id[i]==-1)
id[i]=cnt++;
}
for(i=0; i<edgenum; i++)
{
int s=edge[i].u;
int t=edge[i].v;
edge[i].u=id[s];
edge[i].v=id[t];
if(s!=t)
edge[i].w=edge[i].w-in[t];
}
nodenum=cnt;
root=id[root];
}
return ans;
}
int main()
{
int t,no;
while(~scanf("%d%d%d%d",&n,&x,&y,&z))
{
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=0;
for(int i=1; i<=n; i++)
{
edge[k].u=0;
edge[k].v=i;
edge[k].w=x*p[i].z;
k++;
}
for(int i=1; i<=n; i++)
{
scanf("%d",&t);
for(int j=1; j<=t; j++)
{
scanf("%d",&no);
edge[k].u=i;
edge[k].v=no;
edge[k].w=dis(p[i],p[no])*y;
if(p[i].z<p[no].z)
{
edge[k].w+=z;
}
k++;
}
}
long long int ans=min_cost(0,n+1,k);
if(ans<INF)
printf("%lld\n",ans);
else
printf("poor XiaoA\n");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: