首页 > 代码库 > hdu4009 Transfer water 最小树形图

hdu4009 Transfer water 最小树形图

  每一户人家水的来源有两种打井和从别家接水,每户人家都可能向外输送水。

  打井和接水两种的付出代价都接边。设一个超级源点,每家每户打井的代价就是从该点(0)到该户人家(1~n)的边的权值。接水有两种可能,从高处接水,那么代价是哈密顿距离与Y的乘积(可以认为就是水管的费用);从低处接水,还要加上付出水泵的钱(水管+水泵的费用)。这样就可以建图了。图论,会建图的话问题就解决一半了。

  然后,用模版来解题。不过朱刘算法的模版时间复杂度的差异还是蛮大的。我的模版的建图是邻接矩阵,时间复杂度是O(N^3)。超时,过不了。在网上找了一份按边的起点、终点、权值建图的模版。AC了。该算法的复杂度是O(M)。

#include<iostream>#include<cstdio>#include<algorithm>using namespace std;const int N = 1010, M=1010000,INF=0x3f3f3f3f;int pre[N],id[N],in[N],vis[N];int tot;//边数struct node{    int u,v,w;}e[M];void adde(int i,int j,int k){    e[tot].u=i;e[tot].v=j;e[tot++].w=k;}int zhuliu(int root ,int vn){    int ans=0;    int cnt;    while(1)    {        for(int i=0;i<vn;i++)            in[i]=INF,id[i]=-1,vis[i]=-1;        for(int i=0;i<tot;i++)        {            if(in[e[i].v]>e[i].w && e[i].u!=e[i].v)            {                pre[e[i].v]=e[i].u;                in[e[i].v]=e[i].w;            }        }        in[root]=0;        pre[root]=root;        for(int i=0;i<vn;i++)        {            ans+=in[i];            if(in[i]==INF)//这一步可以看出是否存在这样一棵树形图,因此可以略去DFS                return -1;        }        cnt=0;        for(int i=0;i<vn;i++)        {            if(vis[i]==-1)            {                int t=i;                while(vis[t]==-1)                {                    vis[t]=i;                    t=pre[t];                }                if(vis[t]!=i || t==root) continue;                for(int j=pre[t];j!=t;j=pre[j])                    id[j]=cnt;                id[t]=cnt++;            }        }        if(cnt==0) break;        for(int i=0;i<vn;i++)            if(id[i]==-1)                id[i]=cnt++;        for(int i=0;i<tot;i++)        {            int u,v;            u=e[i].u;            v=e[i].v;            e[i].u=id[u];            e[i].v=id[v];            e[i].w-=in[v];        }        vn=cnt;        root=id[root];    }    return ans;}struct Node{    int a,b,c;}nd[N];int main(){    //freopen("test.txt","r",stdin);    int n,x,y,z,i,j,k,d;    while(scanf("%d%d%d%d",&n,&x,&y,&z)!=EOF)    {        if(!n) break;        tot=0;        for(i=1;i<=n;i++)        {            scanf("%d%d%d",&nd[i].a,&nd[i].b,&nd[i].c);            adde(0,i,nd[i].c*x);        }        for(i=1;i<=n;i++)        {            scanf("%d",&k);            while(k--)            {                scanf("%d",&j);                if(i==j) continue;                d=abs(nd[i].a-nd[j].a)+abs(nd[i].b-nd[j].b)+abs(nd[i].c-nd[j].c);                d*=y;                if(nd[i].c<nd[j].c)  d+=z;                adde(i,j,d);            }        }        int ans=zhuliu(0,n+1);        if(ans==-1) printf("poor XiaoA\n");        else printf("%d\n",ans);    }    return 0;}

 

 

下面是邻接矩阵建图的模版,虽然超时了。但是模版还是有借鉴意义的。模版来自哈工大出版的《图论及应用》。

#include<iostream>#include<cstdio>#include<algorithm>using namespace std;const int N = 1010, INF=0x3f3f3f3f;int Map[N][N];bool vis[N],f[N];//f是缩点标记,1表示该点已经被缩掉int pre[N];int zhuliu(int n){    int sum=0;    int i,j,k;    for(i=0;i<n;i++)    {        f[i]=0;        Map[i][i]=INF;    }    pre[0]=0;    while(1)    {        //求最短弧集合E0        for(i=1;i<n;i++)        {            if(f[i]) continue;            pre[i]=i;            for(j=0;j<n;j++)                if(!f[j]&&Map[j][i]<Map[pre[i]][i])  pre[i]=j;            if(pre[i]==i) return -1; //没有入边,不存在最小树形图        }        //检查E0        for(i=1;i<n;i++)        {            if(f[i]) continue ;            //从当前点开始找环            for(j=0;j<n;j++) vis[j]=0;            vis[0]=1;            j=i;            do            {                vis[j]=1;                j=pre[j];            }            while(!vis[j]) ;            if(!j) continue; //没有找到环            //收缩G中的有向环            i=j;            //将整个环的权值保存,累计入原图的最小树形图            do            {                sum+=Map[pre[j]][j];                j=pre[j];            }            while(j!=i) ;            j=i;            //对与环上的点有关的边,修改边权            do            {                for(k=0; k<n; k++)                    if(!f[k]&&Map[k][j]<INF&&k!=pre[j])                        Map[k][j]-= Map[pre[j]][j];                j=pre[j];            }            while(j!=i) ;            //缩点,将整个环缩成i号点,所有与环上的点有关的边转移到点i            for(j=0;j<n;j++)            {                if(j==i) continue;                for(k=pre[i]; k!=i; k=pre[k])                {                    if(Map[k][j]<Map[i][j]) Map[i][j] =Map[k][j];                    if(Map[j][k]<Map[j][i]) Map[j][i] =Map[j][k];                }            }            //标记环上其他的点为被缩掉            for(j=pre[i];j!=i;j=pre[j]) f[j] =1;            //当前环缩点结束,形成新的图G’,跳出继续求G’的最小树形图            break;        }        //如果所有的点都被检查且没有环存在,现在的最短弧集合E0就是        //最小树形图,累计如sum, 算法结束        if(i==n)        {            for(i=1;i<n;i++) if(!f[i]) sum+=Map[pre[i]][i];            break;        }    }    return sum;}struct node{    int a,b,c;}nd[N];int main(){    //freopen("test.txt","r",stdin);    int n,x,y,z,i,j,k,d;    while(scanf("%d%d%d%d",&n,&x,&y,&z)!=EOF)    {        if(!n) break;        for(i=0;i<=n;i++)            for(j=0;j<i;j++)                Map[j][i]=Map[i][j]=INF;        for(i=1;i<=n;i++)        {            scanf("%d%d%d",&nd[i].a,&nd[i].b,&nd[i].c);            Map[0][i]=nd[i].c*x;        }        for(i=1;i<=n;i++)        {            scanf("%d",&k);            while(k--)            {                scanf("%d",&j);                if(i==j) continue;                d=abs(nd[i].a-nd[j].a)+abs(nd[i].b-nd[j].b)+abs(nd[i].c-nd[j].c);                d*=y;                if(nd[i].c>=nd[j].c)  d+=z;                Map[i][j]=min(d,Map[i][j]);            }        }        printf("%d\n",zhuliu(n+1));    }    return 0;}

 

hdu4009 Transfer water 最小树形图