zoukankan      html  css  js  c++  java
  • 最小树形图(朱刘算法)

    前天做题遇到hdu2121 时一点思路都没有,于是往后面一道题去了,结果做了hdu2122(求最小生成树)之后受到启发,hdu2121分明就是一道求有向图的生成树,也就是最小树形图。。。。

    于是搜了很久,啃了很久,终于……

    主要在这下面三个博客上学习的:

    很赞的图,http://hi.baidu.com/bin183/blog/item/45c37950ece4475f1138c273.html

    很牛逼的理论http://www.zlinkin.com/?p=63

    很好的模板+题目http://www.notonlysuccess.com/index.php/mst/#more-315

    pku3164 Command Network

    定根,求最小树形图,模板题

    View Code
    Source Code
    
    Problem: 3164  User: nanke_ 
    Memory: 276K  Time: 125MS 
    Language: C++  Result: Accepted 
    
    Source Code 
    #include<iostream>
    #include<algorithm>
    #include<string>
    #include<math.h>
    using namespace std;
    const int N = 100+10;
    const int M = 10000+10;
    struct Point
    {
        double x,y;
    }p[N];
    struct edge
    {
        int u,v;
        double cost;
        edge(){}
        edge(int u,int v,double c):u(u),v(v),cost(c){}
    }e[M];
    int pre[N],hash1[N],vis[N];
    double In[N];
    inline double dist(Point& a,Point& b)
    {
        return sqrt(double((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)));
    }
    double Directed_MST(int root,int n,int m)
    {
        double ret=0;
        while(true)
        {
            for(int i=0;i<n;i++)
                In[i]=INT_MAX;
            for(int i=0;i<m;i++)//找最小入边
            {
                int u=e[i].u;
                int v=e[i].v;
                if(e[i].cost<In[v] && u!=v){
                    pre[v]=u;
                    In[v]=e[i].cost;
                }
            }
            for(int i=0;i<n;i++)
            {
                if(i==root)
                    continue;
                if(In[i]==INT_MAX)
                    return -1;
            }
            int cntnode=0;
            memset(hash1,-1,sizeof(hash1));
            memset(vis,-1,sizeof(vis));
            In[root]=0;
            for(int i=0;i<n;i++)//找环
            {
                ret+=In[i];
                int v=i;
                while(vis[v]!=i && hash1[v]==-1 && v!=root)
                {
                    vis[v]=i;
                    v=pre[v];
                }
                if(v!=root && hash1[v]==-1)
                {
                    for(int u=pre[v];u!=v;u=pre[u])
                        hash1[u]=cntnode;
                    hash1[v]=cntnode++;
                }
            }
            if(cntnode==0)
                break;
            for(int i=0;i<n;i++)
                if(hash1[i]==-1)
                    hash1[i]=cntnode++;
            for(int i=0;i<m;i++)//重标记
            {
                int v=e[i].v;
                e[i].u=hash1[e[i].u];
                e[i].v=hash1[e[i].v];
                if(e[i].u!=e[i].v)
                    e[i].cost-=In[v];
            }
            n=cntnode;
            root=hash1[root];
        }
        return ret;
    }
    int main()
    {
        int a,b;
        int n,m;
        while(scanf("%d %d",&n,&m)==2)
        {
            for(int i=0;i<n;i++)
                scanf("%lf %lf",&p[i].x,&p[i].y);
            int mm=0;
            for(int i=0;i<m;i++)
            {
                scanf("%d %d",&a,&b);
                if(a==b)continue;
                a--,b--;
                e[mm++]=edge(a,b,dist(p[a],p[b]));
            }
            double ans=Directed_MST(0,n,mm);
            if(ans==-1)
                puts("poor snoopy");
            else printf("%.2f\n",ans);
        }
        return 0;
    }

    hdu2121  Ice_cream’s world II

    不定根的题目,“只要虚拟一个根连所有的点的权为边权总和+1,最后的结果减去(边权+1)即可”。另外对于求最小根标号,只要搜索被选中的虚边就可以判断了。,与虚根相连的点即为原本的实根!

    如果选择了俩条或俩条以上的与虚根相连的边,则不存在对应的最小树形图

    View Code
    #include<iostream>
    #include<climits>
    #include<math.h>
    using namespace std;
    const int N = 1000+10;
    const int M = N*N;
    struct edge
    {
        int u,v;
        int cost;
    }e[M];
    int pre[N],id[N],visit[N];//id用来标记圈的
    int in[N];//入弧最小的
    int minRoot;
    int Directed_MST(int root,int n,int m)//n表示点数,m表示边数,root表示根
    {
        int u,v,i;
        int ret=0;
        while(true)
        {
            for(i=0;i<n;i++)
                in[i]=INT_MAX;
            for(i=0;i<m;i++)
            {
                u=e[i].u;
                v=e[i].v;
                if(e[i].cost<in[v]&&u!=v)
                {
                    pre[v]=u;//找出每个点的最小入弧
                    if(u==root)
                        minRoot=i;
                    in[v]=e[i].cost;
                }
            }
            for(i=0;i<n;i++)
            {
                if(i==root)
                    continue;
                if(in[i]==INT_MAX){//除根外有个节点无入弧,就返回false
                    return -1;
                }
            }
            in[root]=0;
            int cnt=0;
            memset(id,-1,sizeof(id));
            memset(visit,-1,sizeof(visit));
            for(i=0;i<n;i++)
            {
                ret+=in[i];//进行缩圈
                v=i;
                while(visit[v]!=i&&id[v]==-1&&v!=root)
                {
                    visit[v]=i;
                    v=pre[v];
                }
                if(v!=root&&id[v]==-1)
                {
                    for(u=pre[v];u!=v;u=pre[u])
                        id[u]=cnt;
                    id[v]=cnt++;
                }
            }
            if(cnt==0)
                break;
            for(i=0;i<n;i++)
            {
                if(id[i]==-1)
                    id[i]=cnt++;
            }
            for(i=0;i<m;i++)
            {
                v=e[i].v;//进行缩点,重新标记。
                e[i].u=id[e[i].u];
                e[i].v=id[e[i].v];
                if(e[i].u!=e[i].v)
                    e[i].cost-=in[v];
            }
            n=cnt;
            root=id[root];
        }
        return ret;
    }
    int main()
    {
        int n,m,m1;
        int T,c;
        int r=0;
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            int i,a,b;
            r=0;
            m1=m;
            for(i=0;i<m;i++)
            {
                scanf("%d%d%d",&a,&b,&c);
                e[i].u=a;
                e[i].v=b;
                e[i].cost=c;
                r+=c;
            }
            r++;
            for(i=0;i<n;i++)
            {
                e[m].u=n;
                e[m].v=i;
                e[m].cost=r;
                m++;
            }
            int ans=Directed_MST(n,n+1,m);
            minRoot-=m1;//最小根对应的标号为i-m1
            if(ans==-1||ans>=2*r)
                puts("impossible");
            else
                printf("%d %d\n",ans-r,minRoot);
            puts("");
        }
        return 0;
    }

    hdu4009 Transfer water

    这题目略有区别,却更好理解。。

    对于每一个household 有俩个选择

    1)自己挖井,花费 h*X

    2)通过其他愿意供水的household连一条水管,根据高度有俩种情况的花费

    接下来,对应上面俩种选择,我们先假定一个虚根,那么对应第一种选择,我们可以通过虚根“供水”,虚根往每一个节点连一条边,权值为h*x

    对于第二种选择,则将其与愿意供水的节点连一条边,权值为对应 的花费

    这样,就变成了定根的最小树形图,而且,一定有解,大不了就是每一个household都自己挖井

    View Code
    #include<iostream>
    #include<string>
    #include<algorithm>
    using namespace std;
    const int N = 1000+10;
    const int M = N*N;
    struct Point
    {
        int x,y,z;
    }p[N];
    struct edge
    {
        int u,v,cost;
        edge(){}
        edge(int u,int v,int cost):u(u),v(v),cost(cost){}
    }e[M];
    int pre[N],id[N],in[N],vis[N];
    int X,Y,Z;
    inline int Directed_MST(int root,int n,int m)//n表示点数,m表示边数,root表示根
    {
        int u,v,i;
        int ret=0;
        while(true)
        {
            for(i=0;i<n;i++)
                in[i]=INT_MAX;
            for(i=0;i<m;i++)
            {
                u=e[i].u;
                v=e[i].v;
                if(e[i].cost<in[v]&&u!=v)
                {
                    pre[v]=u;//找出每个点的最小入弧
                    in[v]=e[i].cost;
                }
            }
            for(i=0;i<n;i++)
            {
                if(i==root)
                    continue;
                if(in[i]==INT_MAX){//除根外有个节点无入弧,就返回false
                    return -1;
                }
            }
            in[root]=0;
            int cnt=0;
            memset(id,-1,sizeof(id));
            memset(vis,-1,sizeof(vis));
            for(i=0;i<n;i++)
            {
                ret+=in[i];//进行缩圈
                v=i;
                while(vis[v]!=i&&id[v]==-1&&v!=root)
                {
                    vis[v]=i;
                    v=pre[v];
                }
                if(v!=root&&id[v]==-1)
                {
                    for(u=pre[v];u!=v;u=pre[u])
                        id[u]=cnt;
                    id[v]=cnt++;
                }
            }
            if(cnt==0)
                break;
            for(i=0;i<n;i++)
            {
                if(id[i]==-1)
                    id[i]=cnt++;
            }
            for(i=0;i<m;i++)
            {
                v=e[i].v;//进行缩点,重新标记。
                e[i].u=id[e[i].u];
                e[i].v=id[e[i].v];
                if(e[i].u!=e[i].v)
                    e[i].cost-=in[v];
            }
            n=cnt;
            root=id[root];
        }
        return ret;
    }
    inline int get_cost(Point& a,Point& b)
    {
        int dis=abs(a.x-b.x)+abs(a.y-b.y)+abs(a.z-b.z);
        if(a.z>=b.z)
            return dis*Y;
        return dis*Y+Z;
    }
    int main()
    {
        int n,m,k,a;
        while(scanf("%d %d %d %d",&n,&X,&Y,&Z)==4 && (n||X||Y||Z))
        {
            m=0;
            for(int i=1;i<=n;i++)
                scanf("%d %d %d",&p[i].x,&p[i].y,&p[i].z);
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&k);
                while(k--)
                {
                    scanf("%d",&a);
                    e[m++]=edge(i,a,get_cost(p[i],p[a]));
                }
            }
            for(int i=1;i<=n;i++)
                e[m++]=edge(0,i,p[i].z*X);
            printf("%d\n",Directed_MST(0,n+1,m));
        }
        return 0;
    }
  • 相关阅读:
    react-navigation
    react
    generator-react-webpack
    安装Eclipse反编译插件(jadclipse)
    Python爬虫学习笔记3:基本库的使用
    Python爬虫学习笔记2:爬虫基础
    Python爬虫学习笔记1:request、selenium、ChromeDrive、GeckoDriver等相关依赖安装
    Python学习笔记28:面向对象进阶及 hashlib
    Python学习笔记27:反射,类的内置方法
    Python学习笔记26:封装、@property、@staticmethod和@classmethod装饰器方法、反射getattr()
  • 原文地址:https://www.cnblogs.com/nanke/p/2441725.html
Copyright © 2011-2022 走看看