最小树形图(tju 2248 UVA 11183 poj 3164)

求最小树形图的总权值  

即以固定跟为起点 延给定有向边 可以访问所有的点 并所构成的边权值之和最小 求出这个最小总权值


算法步骤:
① 清除自环,输入的时候判断即可
② 先判断从固定根开始是否可达所有原图中的点。简单搜索加标记位就可以。如果不可就不用说了,肯定没戏。
③ 为除根之外的每个点选定一条最小入边。
(记pre [vi]为该边的起点)
④ 判断这个入边集是否存在有向环,如果不存在,我们很容易证明这个集合就是该图的最小树形图,转⑥,否则接⑤(利用prev数组,枚举为检查过的点作为搜索的起点,做类似DFS的操作)
⑤ 消环。设(u,i,w)表示从u到i的权为w的边。设刚才的有向环缩为新结点new。若u位于环上,并设环中指向u的边权是in[u]。那么对于每条从u出发的边(u, i, w),在新图中连接(new, i, w)的边,其中new为新加的人工顶点; 对于每条进入u的边(i, u, w),在新图中建立边(i, new, w-in[u])的边。新图中最小树形图的权加上旧图中被收缩的那个环的权和,就是原图中最小树形图的权。重复③④⑤
⑥ 成功,返回DMST总权值。
补充1:如果无固定根,增加一个节点,连接到所有节点,并且距离一样,即可转化为有固定根。
补充2:本算法只能求最小总权值,但不能求路径。


tju 2248 http://acm.tju.edu.cn/toj/showp2248.html

裸的最小树形图

<span style="font-family:KaiTi_GB2312;font-size:18px;">#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>
#include <vector>
#include <queue>

#define MEM(a,x) memset(a,x,sizeof a)
#define eps 1e-8
#define MOD 10009
#define MAXN 1010
#define MAXM 42010
#define INF 99999999
#define ll __int64
#define bug cout<<"here"<<endl
#define fread freopen("ceshi.txt","r",stdin)
#define fwrite freopen("out.txt","w",stdout)

using namespace std;

int Read()
{
    char c = getchar();
    while (c < '0' || c > '9') c = getchar();
    int x = 0;
    while (c >= '0' && c <= '9') {
        x = x * 10 + c - '0';
        c = getchar();
    }
    return x;
}

void Print(int a)
{
     if(a>9)
         Print(a/10);
     putchar(a%10+'0');
}

struct Edge
{
    int u,v;
    int cost;
}edge[MAXM];
int pre[MAXN],vis[MAXN],id[MAXN];
int in[MAXN];

int zhuliu(int root,int n,int m,Edge edge[])
{
    int res=0;
    int u,v;
    while(1)
    {
        for(int i=0;i<n;i++)
            in[i]=INF;
        for(int i=0;i<m;i++)
            if(edge[i].u!=edge[i].v&&(edge[i].cost-in[edge[i].v]<eps))
        {
            pre[edge[i].v]=edge[i].u;
            in[edge[i].v]=edge[i].cost;
        }
        for(int i=0;i<n;i++)
            if(i!=root&&in[i]==INF)
                return -1;
        int tn=0;
        MEM(id,-1); MEM(vis,-1);
        in[root]=0;
        for(int i=0;i<n;i++)
        {
            res+=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(int u=pre[v];u!=v;u=pre[u])
                    id[u]=tn;
                id[v]=tn++;
            }
        }
        if(tn==0) break;//没有有向环
        for(int i=0;i<n;i++)
            if(id[i]==-1)
                id[i]=tn++;
        for(int i=0;i<m;)
        {
            v=edge[i].v;
            edge[i].u=id[edge[i].u];
            edge[i].v=id[edge[i].v];
            if(edge[i].u!=edge[i].v)
                edge[i++].cost-=in[v];
            else
                swap(edge[i],edge[--m]);
        }
        n=tn;
        root=id[root];
    }
    return res;
}
int g[MAXN][MAXN];
int x[MAXN],y[MAXN];



int main()
{
//    fread;
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0&&m==0) break;
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                g[i][j]=INF;
        int u,v,cost;
        while(m--)
        {
            scanf("%d%d%d",&u,&v,&cost);
            u--; v--;
            if(u==v) continue;
            g[u][v]=min(g[u][v],cost);
        }
        int num=0;
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                if(g[i][j]<INF)
                {
                    edge[num].u=i;
                    edge[num].v=j;
                    edge[num++].cost=g[i][j];
                }
        int ans=zhuliu(0,n,num,edge);
        if(ans==-1) printf("impossible\n");
        else printf("%d\n",ans);
    }
    return 0;
}
</span>

UVA 11183 http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=18548

<span style="font-family:KaiTi_GB2312;font-size:18px;">#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>
#include <vector>
#include <queue>

#define MEM(a,x) memset(a,x,sizeof a)
#define eps 1e-8
#define MOD 10009
#define MAXN 1010
#define MAXM 42010
#define INF 99999999
#define ll __int64
#define bug cout<<"here"<<endl
#define fread freopen("ceshi.txt","r",stdin)
#define fwrite freopen("out.txt","w",stdout)

using namespace std;

int Read()
{
    char c = getchar();
    while (c < '0' || c > '9') c = getchar();
    int x = 0;
    while (c >= '0' && c <= '9') {
        x = x * 10 + c - '0';
        c = getchar();
    }
    return x;
}

void Print(int a)
{
     if(a>9)
         Print(a/10);
     putchar(a%10+'0');
}

struct Edge
{
    int u,v;
    int cost;
}edge[MAXM];
int pre[MAXN],vis[MAXN],id[MAXN];
int in[MAXN];

int zhuliu(int root,int n,int m,Edge edge[])
{
    int res=0;
    int u,v;
    while(1)
    {
        for(int i=0;i<n;i++)
            in[i]=INF;
        for(int i=0;i<m;i++)
            if(edge[i].u!=edge[i].v&&(edge[i].cost-in[edge[i].v]<eps))
        {
            pre[edge[i].v]=edge[i].u;
            in[edge[i].v]=edge[i].cost;
        }
        for(int i=0;i<n;i++)
            if(i!=root&&in[i]==INF)
                return -1;
        int tn=0;
        MEM(id,-1); MEM(vis,-1);
        in[root]=0;
        for(int i=0;i<n;i++)
        {
            res+=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(int u=pre[v];u!=v;u=pre[u])
                    id[u]=tn;
                id[v]=tn++;
            }
        }
        if(tn==0) break;//没有有向环
        for(int i=0;i<n;i++)
            if(id[i]==-1)
                id[i]=tn++;
        for(int i=0;i<m;)
        {
            v=edge[i].v;
            edge[i].u=id[edge[i].u];
            edge[i].v=id[edge[i].v];
            if(edge[i].u!=edge[i].v)
                edge[i++].cost-=in[v];
            else
                swap(edge[i],edge[--m]);
        }
        n=tn;
        root=id[root];
    }
    return res;
}
int g[MAXN][MAXN];
int x[MAXN],y[MAXN];



int main()
{
//    fread;
    int tc;
    int cs=1;
    scanf("%d",&tc);
    while(tc--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                g[i][j]=INF;
        int u,v,cost;
        while(m--)
        {
            scanf("%d%d%d",&u,&v,&cost);
            if(u==v) continue;
            g[u][v]=min(g[u][v],cost);
        }
        int num=0;
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                if(g[i][j]<INF)
                {
                    edge[num].u=i;
                    edge[num].v=j;
                    edge[num++].cost=g[i][j];
                }
        int ans=zhuliu(0,n,num,edge);
        printf("Case #%d: ",cs++);
        if(ans==-1) printf("Possums!\n");
        else printf("%d\n",ans);
    }
    return 0;
}
</span>

poj 3164 http://poj.org/problem?id=3164

此题中cost是两个点之间的距离 

<span style="font-family:KaiTi_GB2312;font-size:18px;">#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>
#include <vector>
#include <queue>

#define MEM(a,x) memset(a,x,sizeof a)
#define eps 1e-8
#define MOD 10009
#define MAXN 1010
#define MAXM 12010
#define INF 99999999
#define ll __int64
#define bug cout<<"here"<<endl
#define fread freopen("ceshi.txt","r",stdin)
#define fwrite freopen("out.txt","w",stdout)

using namespace std;

int Read()
{
    char c = getchar();
    while (c < '0' || c > '9') c = getchar();
    int x = 0;
    while (c >= '0' && c <= '9') {
        x = x * 10 + c - '0';
        c = getchar();
    }
    return x;
}

void Print(int a)
{
     if(a>9)
         Print(a/10);
     putchar(a%10+'0');
}

struct Edge
{
    int u,v;
    double cost;
}edge[MAXM];
int pre[MAXN],vis[MAXN],id[MAXN];
double in[MAXN];

double zhuliu(int root,int n,int m,Edge edge[])
{
    double res=0;
    int u,v;
    while(1)
    {
        for(int i=0;i<n;i++)
            in[i]=INF;
        for(int i=0;i<m;i++)
            if(edge[i].u!=edge[i].v&&(edge[i].cost-in[edge[i].v]<eps))
        {
            pre[edge[i].v]=edge[i].u;
            in[edge[i].v]=edge[i].cost;
        }
        for(int i=0;i<n;i++)
            if(i!=root&&in[i]==INF)
                return -1;
        int tn=0;
        MEM(id,-1); MEM(vis,-1);
        in[root]=0;
        for(int i=0;i<n;i++)
        {
            res+=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(int u=pre[v];u!=v;u=pre[u])
                    id[u]=tn;
                id[v]=tn++;
            }
        }
        if(tn==0) break;//没有有向环
        for(int i=0;i<n;i++)
            if(id[i]==-1)
                id[i]=tn++;
        for(int i=0;i<m;)
        {
            v=edge[i].v;
            edge[i].u=id[edge[i].u];
            edge[i].v=id[edge[i].v];
            if(edge[i].u!=edge[i].v)
                edge[i++].cost-=in[v];
            else
                swap(edge[i],edge[--m]);
        }
        n=tn;
        root=id[root];
    }
    return res;
}
double g[MAXN][MAXN];
int x[MAXN],y[MAXN];

double dis(int u,int v)
{
    return sqrt((double)((x[u]-x[v])*(x[u]-x[v])+(y[u]-y[v])*(y[u]-y[v])));
}

int main()
{
//    fread;
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                g[i][j]=INF;
        for(int i=0;i<n;i++)
            scanf("%d%d",&x[i],&y[i]);
        while(m--)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            u--; v--;
            if(u==v) continue;
            double len=dis(u,v);
            if(g[u][v]-len>eps)
                g[u][v]=len;
        }
        int num=0;
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                if(g[i][j]<INF)
        {
            edge[num].u=i; edge[num].v=j; edge[num++].cost=g[i][j];
        }
        double ans=zhuliu(0,n,num,edge);
        if(ans==-1) printf("poor snoopy\n");
        else printf("%.2lf\n",ans);
    }
    return 0;
}
</span>



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值