zoukankan      html  css  js  c++  java
  • hdu4009 Transfer water 最小树形图

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

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

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

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<algorithm>
      4 using namespace std;
      5 const int N = 1010, M=1010000,INF=0x3f3f3f3f;
      6 int pre[N],id[N],in[N],vis[N];
      7 int tot;//边数
      8 struct node
      9 {
     10     int u,v,w;
     11 }e[M];
     12 void adde(int i,int j,int k)
     13 {
     14     e[tot].u=i;e[tot].v=j;e[tot++].w=k;
     15 }
     16 int zhuliu(int root ,int vn)
     17 {
     18     int ans=0;
     19     int cnt;
     20     while(1)
     21     {
     22         for(int i=0;i<vn;i++)
     23             in[i]=INF,id[i]=-1,vis[i]=-1;
     24         for(int i=0;i<tot;i++)
     25         {
     26             if(in[e[i].v]>e[i].w && e[i].u!=e[i].v)
     27             {
     28                 pre[e[i].v]=e[i].u;
     29                 in[e[i].v]=e[i].w;
     30             }
     31         }
     32         in[root]=0;
     33         pre[root]=root;
     34         for(int i=0;i<vn;i++)
     35         {
     36             ans+=in[i];
     37             if(in[i]==INF)//这一步可以看出是否存在这样一棵树形图,因此可以略去DFS
     38                 return -1;
     39         }
     40         cnt=0;
     41         for(int i=0;i<vn;i++)
     42         {
     43             if(vis[i]==-1)
     44             {
     45                 int t=i;
     46                 while(vis[t]==-1)
     47                 {
     48                     vis[t]=i;
     49                     t=pre[t];
     50                 }
     51                 if(vis[t]!=i || t==root) continue;
     52                 for(int j=pre[t];j!=t;j=pre[j])
     53                     id[j]=cnt;
     54                 id[t]=cnt++;
     55             }
     56         }
     57         if(cnt==0) break;
     58         for(int i=0;i<vn;i++)
     59             if(id[i]==-1)
     60                 id[i]=cnt++;
     61         for(int i=0;i<tot;i++)
     62         {
     63             int u,v;
     64             u=e[i].u;
     65             v=e[i].v;
     66             e[i].u=id[u];
     67             e[i].v=id[v];
     68             e[i].w-=in[v];
     69         }
     70         vn=cnt;
     71         root=id[root];
     72     }
     73     return ans;
     74 }
     75 
     76 struct Node
     77 {
     78     int a,b,c;
     79 }nd[N];
     80 int main()
     81 {
     82     //freopen("test.txt","r",stdin);
     83     int n,x,y,z,i,j,k,d;
     84     while(scanf("%d%d%d%d",&n,&x,&y,&z)!=EOF)
     85     {
     86         if(!n) break;
     87         tot=0;
     88         for(i=1;i<=n;i++)
     89         {
     90             scanf("%d%d%d",&nd[i].a,&nd[i].b,&nd[i].c);
     91             adde(0,i,nd[i].c*x);
     92         }
     93         for(i=1;i<=n;i++)
     94         {
     95             scanf("%d",&k);
     96             while(k--)
     97             {
     98                 scanf("%d",&j);
     99                 if(i==j) continue;
    100                 d=abs(nd[i].a-nd[j].a)+abs(nd[i].b-nd[j].b)+abs(nd[i].c-nd[j].c);
    101                 d*=y;
    102                 if(nd[i].c<nd[j].c)  d+=z;
    103                 adde(i,j,d);
    104             }
    105         }
    106         int ans=zhuliu(0,n+1);
    107         if(ans==-1) printf("poor XiaoA
    ");
    108         else printf("%d
    ",ans);
    109     }
    110     return 0;
    111 }
    View Code

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

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<algorithm>
      4 using namespace std;
      5 const int N = 1010, INF=0x3f3f3f3f;
      6 int Map[N][N];
      7 bool vis[N],f[N];
      8 //f是缩点标记,1表示该点已经被缩掉
      9 int pre[N];
     10 int zhuliu(int n)
     11 {
     12     int sum=0;
     13     int i,j,k;
     14     for(i=0;i<n;i++)
     15     {
     16         f[i]=0;
     17         Map[i][i]=INF;
     18     }
     19     pre[0]=0;
     20     while(1)
     21     {
     22         //求最短弧集合E0
     23         for(i=1;i<n;i++)
     24         {
     25             if(f[i]) continue;
     26             pre[i]=i;
     27             for(j=0;j<n;j++)
     28                 if(!f[j]&&Map[j][i]<Map[pre[i]][i])  pre[i]=j;
     29             if(pre[i]==i) return -1; //没有入边,不存在最小树形图
     30         }
     31         //检查E0
     32         for(i=1;i<n;i++)
     33         {
     34             if(f[i]) continue ;
     35             //从当前点开始找环
     36             for(j=0;j<n;j++) vis[j]=0;
     37             vis[0]=1;
     38             j=i;
     39             do
     40             {
     41                 vis[j]=1;
     42                 j=pre[j];
     43             }
     44             while(!vis[j]) ;
     45             if(!j) continue; //没有找到环
     46             //收缩G中的有向环
     47             i=j;
     48             //将整个环的权值保存,累计入原图的最小树形图
     49             do
     50             {
     51                 sum+=Map[pre[j]][j];
     52                 j=pre[j];
     53             }
     54             while(j!=i) ;
     55             j=i;
     56             //对与环上的点有关的边,修改边权
     57             do
     58             {
     59                 for(k=0; k<n; k++)
     60                     if(!f[k]&&Map[k][j]<INF&&k!=pre[j])
     61                         Map[k][j]-= Map[pre[j]][j];
     62                 j=pre[j];
     63             }
     64             while(j!=i) ;
     65             //缩点,将整个环缩成i号点,所有与环上的点有关的边转移到点i
     66             for(j=0;j<n;j++)
     67             {
     68                 if(j==i) continue;
     69                 for(k=pre[i]; k!=i; k=pre[k])
     70                 {
     71                     if(Map[k][j]<Map[i][j]) Map[i][j] =Map[k][j];
     72                     if(Map[j][k]<Map[j][i]) Map[j][i] =Map[j][k];
     73                 }
     74             }
     75             //标记环上其他的点为被缩掉
     76             for(j=pre[i];j!=i;j=pre[j]) f[j] =1;
     77             //当前环缩点结束,形成新的图G’,跳出继续求G’的最小树形图
     78             break;
     79         }
     80         //如果所有的点都被检查且没有环存在,现在的最短弧集合E0就是
     81         //最小树形图,累计如sum, 算法结束
     82         if(i==n)
     83         {
     84             for(i=1;i<n;i++) if(!f[i]) sum+=Map[pre[i]][i];
     85             break;
     86         }
     87     }
     88     return sum;
     89 }
     90 
     91 struct node
     92 {
     93     int a,b,c;
     94 }nd[N];
     95 int main()
     96 {
     97     //freopen("test.txt","r",stdin);
     98     int n,x,y,z,i,j,k,d;
     99     while(scanf("%d%d%d%d",&n,&x,&y,&z)!=EOF)
    100     {
    101         if(!n) break;
    102         for(i=0;i<=n;i++)
    103             for(j=0;j<i;j++)
    104                 Map[j][i]=Map[i][j]=INF;
    105         for(i=1;i<=n;i++)
    106         {
    107             scanf("%d%d%d",&nd[i].a,&nd[i].b,&nd[i].c);
    108             Map[0][i]=nd[i].c*x;
    109         }
    110         for(i=1;i<=n;i++)
    111         {
    112             scanf("%d",&k);
    113             while(k--)
    114             {
    115                 scanf("%d",&j);
    116                 if(i==j) continue;
    117                 d=abs(nd[i].a-nd[j].a)+abs(nd[i].b-nd[j].b)+abs(nd[i].c-nd[j].c);
    118                 d*=y;
    119                 if(nd[i].c>=nd[j].c)  d+=z;
    120                 Map[i][j]=min(d,Map[i][j]);
    121             }
    122         }
    123         printf("%d
    ",zhuliu(n+1));
    124     }
    125     return 0;
    126 }
    View Code
  • 相关阅读:
    AD 485、422电路
    AD 差分
    思维导图
    68 二叉树的最近公共祖先
    65. 不用加减乘除做加法
    64. 求1+2+…+n
    10- I. 斐波那契数列
    11&12. 旋转数组的最小数字
    12. 矩阵中的路径
    13. 机器人的运动范围
  • 原文地址:https://www.cnblogs.com/Potato-lover/p/3942321.html
Copyright © 2011-2022 走看看