最小生成树——单点度数限制

问题

求最小生成树,满足结点1的度数不超过k的情况下,使边的权值和最小。

思路

先去掉结点1,用Kruskal生成一个森林,设其有m个联通块。
如果 k<m k < m ,则显然无解。
从结点1往每个联通块连最小的那条边,得到结点1度数为m时的最小生成树

现在要使其扩展到k度

枚举每一条与结点1相连,没有使用的边,试图把它加进生成树,删掉形成的环上最大的边(破圈算法),计算新的生成树权值,选择最优的一条边加入。这样操作使得新的生成树结点1度数+1。

找环上最大边可以先用DP预处理,防止每次枚举边都要 O(n) O ( n ) 找环

重复执行以上操作直到结点1度数为k,或者操作无法使得生成树更优。

题目

POJ1639

#include<iostream>
#include<string>
#include<map>
#include<vector>
#include<algorithm>
using namespace std;
const int MAXN=22,MAXE=MAXN*MAXN;

struct Edge
{
    int u,v,len;
    Edge(){}
    Edge(int _u,int _v,int _len)
    {u=_u;v=_v;len=_len;}
    bool operator < (const Edge &t)const
    {return len<t.len;}
};
struct AdjEdge
{
    int v,len,id;
    AdjEdge(){}
    AdjEdge(int _v,int _len,int _id)
    {v=_v;len=_len;id=_id;}
};

int dsu[MAXN];
int Root(int x)
{
    if(dsu[x]==0)
        return x;
    return (dsu[x]=Root(dsu[x]));
}

int n,m,K;
Edge edge[MAXE];

vector<AdjEdge> adj[MAXN];
void AddEdge(int i)
{
    Edge e=edge[i];
    adj[e.u].push_back(AdjEdge(e.v,e.len,i));
    adj[e.v].push_back(AdjEdge(e.u,e.len,i));
}

int Kruskal()
{
    sort(edge+1,edge+m+1);
    int ans=0;
    for(int i=1;i<=m;i++)
    {
        if(edge[i].u==1||edge[i].v==1)
            continue;
        int r1=Root(edge[i].u),r2=Root(edge[i].v);
        if(r1==r2)
            continue;
        AddEdge(i);
        dsu[r1]=r2;
        ans+=edge[i].len;
    }
    return ans;
}

bool del[MAXE];
int dp[MAXN],dpid[MAXN];
void DP(int u,int f=0)
{
    for(int i=0;i<(int)adj[u].size();i++)
    {
        int v=adj[u][i].v,eid=adj[u][i].id,len=adj[u][i].len;
        if(v==f||del[eid])
            continue;
        if(u!=1)
        {
            if(dp[u]>len)
                dp[v]=dp[u],dpid[v]=dpid[u];
            else
                dp[v]=len,dpid[v]=eid;
        }
        else
            dp[v]=0;
        DP(v,u);
    }
}

map<string,int> id;

int blkc;
int minE[MAXN];

int main()
{
    id["Park"]=++n;
    cin>>m;
    for(int i=1;i<=m;i++)
    {
        string u,v;
        int len;
        cin>>u>>v>>len;
        if(id.count(u)==0)
            id[u]=++n;
        if(id.count(v)==0)
            id[v]=++n;
        edge[i]=Edge(id[u],id[v],len);
    }
    cin>>K;

    int ans=Kruskal();

    for(int i=1;i<=m;i++)
    {
        if(edge[i].u!=1&&edge[i].v!=1)
            continue;
        int v=(edge[i].u==1?edge[i].v:edge[i].u);
        int r=Root(v);
        if(minE[r]==0)
        {
            blkc++;
            minE[r]=i;
        }
    }
    for(int i=1;i<=n;i++)
        if(minE[i]!=0)
        {
            ans+=edge[minE[i]].len;
            AddEdge(minE[i]);
        }

    for(int i=blkc+1;i<=K;i++)
    {
        DP(1);
        int best=0x3F3F3F3F,e,e1;
        for(int j=1;j<=m;j++)
        {
            if(del[j]||(edge[j].u!=1&&edge[j].v!=1))
                continue;
            int v=(edge[j].u==1?edge[j].v:edge[j].u);
            if(best>edge[j].len-dp[v])
                best=edge[j].len-dp[v],e=dpid[v],e1=j;
        }
        if(best>=0)
            break;
        ans+=best;
        AddEdge(e1);
        del[e]=true;
    }

    cout<<"Total miles driven: "<<ans<<endl;

    return 0;
}
### 回答1: Kruskal算法和Prim算法都可以求解任何一个带权无向连通图的最小生成树。其中,Kruskal算法基于贪心思想,通过不断选择边权值最小且不会形成环的边来构建最小生成树;而Prim算法则是从一个起开始,每次选择与当前生成树相邻且权值最小的边加入生成树中,直到生成树包含所有节为止。两种算法的时间复杂度均为O(ElogE),其中E为边数。 ### 回答2: 任何一个带权无向连通图的最小生成树是指在该图中找到一棵包含所有节生成树,使得该生成树的边权之和最小。 为了更好地理解最小生成树,我们可以以一个具体的例子来说明。假设有如下一张无向图: ![image.png](https://cdn.luogu.com.cn/upload/pic/24255.png) 这张图中有6个节和7条边。如果我们需要在该图中找到一棵包含所有节生成树,则可以得到如下几个解: - 选择边(1, 2)、(2, 3)、(2, 4)、(3, 5)、(4, 6),生成的树的边权之和为7+2+4+5+1=19 - 选择边(1, 2)、(2, 3)、(2, 4)、(4, 5)、(6, 4),生成的树的边权之和为7+2+4+3+1=17 - 选择边(1, 2)、(2, 4)、(3, 5)、(4, 5)、(4, 6),生成的树的边权之和为7+4+5+3+1=20 可以发现,虽然以上三个解都是包含所有节生成树,但其边权之和是不同的。其中,第二个解的边权之和最小,可以称其为该图的最小生成树。 从上述例子中可以看出,在寻找最小生成树时,我们需要在生成树中选择边的过程中,不断地计算边权之和,同时确保所生成的树包含图中所有的节。这种算法中常用到的是Kruskal算法和Prim算法。 Kruskal算法依据的是贪心策略,每次选择边权最小并且不与已选择的边构成环的边,依次将这些边加入生成树中。最终的生成树即为最小生成树。 Prim算法也是一种贪心算法,其选择边的方式与Kruskal算法不同。Prim算法从任一节出发,每次将与当前生成树距离最短的未选择的节连接起来,逐步扩大生成树的范围,直到所有节都被包含在生成树中。最终的生成树即为最小生成树。 总之,最小生成树是一个经典的图论问题,在实际应用中具有广泛的价值和意义。了解并掌握相应的算法,可以有效地解决实际问题,提高数据处理的效率。 ### 回答3: 最小生成树,也被称为MST(Minimum Spanning Tree),是指在一张带权图中,将所有节彼此连接起来且总权值最小的树。在实际应用中,最小生成树可以帮助我们寻找最优的物流路径、路网系统等问题。 任何一个带权无向连通图的最小生成树,可以使用Prim算法或Kruskal算法来计算。这两种算法都是贪心算法,用来选择权值最小的边来构建最小生成树。 Prim算法基于节,从一个固定的起开始构建最小生成树,每次在当前生成树中找到最近的未加入节,然后加入这个节到当前树中去。Prim算法通过建立一个优先队列,不断地选取权值最小的边来构建最小生成树。 Kruskal算法基于边,将所有边按照权值从小到大排序,每次选择一条没有形成环的边加入生成树中。如果新加入一条边会形成环,则不加入这条边,并选择一条权值更小的边。Kruskal算法通过并查集来判断是否产生环,并在遍历完所有边之后得到最小生成树。 需要注意的是,如果带权图不是连通图,那么最小生成树就不存在。如果要处理非连通图,可以先把图进行连通分量的划分,然后再对每个连通分量分别求最小生成树。 总之,无论是Prim算法还是Kruskal算法,对于任何一个带权无向连通图,都可以用贪心算法来求出最小生成树。这样的最小生成树可以帮助我们寻找最优的路径,优化网络,使得节之间连接更加紧密,大大提高了系统的可靠性和效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值