HDU4009 Transfer water【最小树形图算法详解】

最小树形图

最小树形图实际上可以理解为有向图的最小生成树,它满足这样的要求:

1、不存在环;

2、除根结点的入度为0,其他点的入度都为1;

3、满足1和2的条件的所有子图中权值和最小;

朱刘算法的基本思想

每次找最小入边集,如果有环或不连通,环缩点重新建图再继续重复;

0、初始化:每个点入边最小权值初始化为极大值;

1、找最小入边集:就是找到每一个点所有入边里权值最小的那个,并记录下权值和前驱点;

2、找非根无入边点:如果能找到说明这个图不存在最小树形图;

3、找环 缩点:每个点从前驱点向前找,要么找到根,要么找到一个环,如果找到一个环,那么把环缩成一个点,更新节点编号;

4、重新构图:依据当前的图和缩点后的点建立新的图,重复上述步骤直到最终无环;

重新建图每条边的权值如何确定?每个点其余入边的权值减去最小入边的权值即可,因为我们只要权值和最小;

举个例子

第一次找到最小入边集找到(3,4)和(4,3),显然是一个环,这两条边要舍弃一条,按照上面说的:

3,4缩点带来的代价是2+4=6

对于3:(1,3)变为5-2=3,那么1->3->4的权值就是 6+3=9=5+4;

对于4:(2,4)变为6-4=2,那么2->4->3的权值就是 6+2=8=2+6;

把缩点中的多余代价在其他入边减掉,就相当于没有选这条边;

例题

题目链接:HDU4009 Transfer wate

题意:n个点建水井的代价是x*高度,从别家借水的代价是y*两点的曼哈顿距离,如果比供水点的高度高,要再加z;

分析:0点向所有点建x*高度的边;供水点向借水点按题意建边;

#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
const int maxn=1007;
const int maxm=1e6+7;
const int mod=1e9+7;
const int INF=0x7f7f7f7f;
int n,X,Y,Z,m;
struct point{int x,y,z;} p[maxn];
struct edge{int u,v,w;} g[maxm];
int pre[maxn],id[maxn],vis[maxn],in[maxn];
int cal(int i,int j)
{
    if(i==j) return INF;
    int tmp=abs(p[i].x-p[j].x)+abs(p[i].y-p[j].y)+abs(p[i].z-p[j].z);
    if(p[i].z<p[j].z) return tmp*Y+Z;
    return tmp*Y;
}
ll Directed_MST(int rt,int V,int E)
{
    ll res=0;
    while(1)
    {
        //0、初始化
        for(int i=0;i<V;i++) in[i]=INF;
        //1、找最小入边集
        for(int i=0;i<E;i++)
        {
            int u=g[i].u,v=g[i].v;
            if(g[i].w<in[v] && u!=v) {pre[v]=u;in[v]=g[i].w;}
        }
        //2、找非根无入边点(这题不需要)
        for(int i=0;i<V;i++)
        {
            if(i==rt) continue;
            if(in[i]==INF) return -1;
        }
        //3、找环 缩点(注意这里的V和id!!)
        int cnt=0;
        memset(id,-1,sizeof(id));
        memset(vis,-1,sizeof(vis));
        in[rt]=0;
        for(int i=0;i<V;i++)
        {
            res+=1ll*in[i];int v=i;
            while(vis[v]!=i && id[v]==-1 && v!=rt) {vis[v]=i;v=pre[v];}
            //每个点寻找其前驱点,要么最后到根,要么找到一个环
            if(v!=rt && id[v]==-1)
            {
                for(int u=pre[v];u!=v;u=pre[u]) id[u]=cnt;
                id[v]=cnt++;
            }//缩点
        }
        if(cnt==0) return res;
        //4、重新构图
        for(int i=0;i<V;i++) if(id[i]==-1) id[i]=cnt++;
        for(int i=0;i<E;i++)
        {
            int u=g[i].u,v=g[i].v,w=g[i].w;
            if(id[u]!=id[v]) w-=in[v];
            g[i]={id[u],id[v],w};
        }
        V=cnt;rt=id[rt];
    }
    return res;
}
int main()
{
    while(~scanf("%d%d%d%d",&n,&X,&Y,&Z))
    {
        if(!n && !X && !Y && !Z) break;
        m=0;
        for(int i=1;i<=n;i++) 
        {
            scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].z);
            g[m++]={0,i,X*p[i].z};
        }
        for(int i=1;i<=n;i++)
        {
            int k;scanf("%d",&k);
            while(k--)
            {
                int j;scanf("%d",&j);
                g[m++]={i,j,cal(i,j)};
            }
        }
        ll ans=Directed_MST(0,n+1,m);
        if(ans==-1) printf("poor XiaoA\n");
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_43813163/article/details/104545236