现在的位置: 首页 > 综合 > 正文

最小树形图(朱刘算法)

2012年07月09日 ⁄ 综合 ⁄ 共 5080字 ⁄ 字号 评论关闭

前天做题遇到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;
}

 

抱歉!评论已关闭.