HDU4009 Transfer water [最小树形图]

本文介绍了一种求解最小树形图问题的算法实现,包括寻找最小入弧、检测并压缩有向环等关键步骤,并提供了一个具体的C语言代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  最小树形图模版题,学习了一下最小树形图。

  三步走,找最小入弧,找有向环,缩环为点。

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define INF 0x3f3f3f3f
#define MAXN 1001
struct edge{
    int u,v,w;
}e[MAXN*MAXN];
int n,x,y,z,es,k,tt;
int pnt[MAXN][3];
int dist(int p1,int p2){
    int ret=0;
    for(int i=0;i<3;i++)ret+=abs(pnt[p1][i]-pnt[p2][i]);
    return ret;
}
void addedge(int u,int v,int w){
    e[es].u=u,e[es].v=v,e[es].w=w,es++;
}
/*点和边都是从0开始的,root包含在点集中*/
int in[MAXN],pre[MAXN],id[MAXN],vis[MAXN];
int dir_mst(int root,int vs,int es){
    int ret=0;
    for(;;){
        int size=vs*4;
        memset(in,0x3f,size);
        memset(id,-1,size);
        memset(vis,-1,size);
        //step1:找最小入弧
        for(int i=0;i<es;i++){
            int u=e[i].u,v=e[i].v,w=e[i].w;
            if(w>=in[v]||u==v)continue;
            pre[v]=u,in[v]=w;
        }
        in[root]=0,pre[root]=root;
            //如果有孤立点说明无解
        for(int i=0;i<vs;i++){
            ret+=in[i];
            if(in[i]==INF)return -1;
        }
        //step2:找有向环
        int idx=0;
        for(int i=0;i<vs;i++)if(vis[i]==-1){
            int u=i;
            while(vis[u]==-1)vis[u]=i,u=pre[u];
            if(vis[u]!=i||u==root)continue;
            for(int t=pre[u];t!=u;t=pre[t])id[t]=idx;
            id[u]=idx++;
        }
            //没有环,已经完成
        if(idx==0)break;
        for(int i=0;i<vs;i++)if(id[i]==-1)id[i]=idx++;
        //step3:将环缩为点
        for(int i=0;i<es;i++){
            e[i].w-=in[e[i].v];
            e[i].u=id[e[i].u];
            e[i].v=id[e[i].v];
        }
        vs=idx;
        root=id[root];
    }
    return ret;
}
int main(){
    freopen("test.in","r",stdin);
    while(scanf("%d%d%d%d",&n,&x,&y,&z),n||x||y||z){
        for(int i=1;i<=n;i++)
            scanf("%d%d%d",&pnt[i][0],&pnt[i][1],&pnt[i][2]);
        es=0;
        for(int i=1;i<=n;i++)
            addedge(0,i,pnt[i][2]*x);
        for(int i=1;i<=n;i++){
            scanf("%d",&k);
            while(k--){
                scanf("%d",&tt);
                if(pnt[i][2]>=pnt[tt][2])addedge(i,tt,dist(i,tt)*y);
                else addedge(i,tt,dist(i,tt)*y+z);
            }
        }
        int res=dir_mst(0,n+1,es);
        if(res==-1)printf("poor XiaoA\n");
        else printf("%d\n",res);
    }
}

转载于:https://www.cnblogs.com/swm8023/archive/2012/08/27/2659264.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值