最小k度限制生成树

/*************************************************
算法引入:
最小k度限制生成树,就是指有特殊的某一点的度不能超过k时的最小生成树;
如果T是G的一个生成树且dT(v0)=k,则称T为G的k度限制生成树;
G中权值和最小的k度限制生成树称为G的最小k度生成树;

算法思想:
设特殊的那点为v0,先把v0删除,求出剩下连通图的所有最小生成树;
假如有m棵最小生成树,那么这些生成树必定要跟v0点相连;
也就是说这棵生成树的v0点至少是m度的;
若m>k,条件不成立,无法找到最小k度限制生成树;
若m<=k,则枚举m到k的所有最小生成树,即一步步将v0点的度加1,直到v0点的度为k为止;
则v0点度从m到k的(k-m+1)棵最小生成树中最小的那棵即为答案;

算法步骤:
(1)先求出最小m度限制生成树:
原图中去掉和V0相连的所有边(可以先存两个图,建议一个邻接矩阵,一个邻接表,用方便枚举边的邻接表来构造新图);
得到m个连通分量,则这m个连通分量必须通过v0来连接;
则在图G的所有生成树中dT(v0)>=m;
则当k
#include
#include
#include
#include
#include
using namespace std;

const int INF=99999999;
const int N=100;

int n,m;//n为边的数量,m表示限度值
int cnt;//计算出来的结点数
int set[N];
bool flag[N][N];
int G[N][N];
int ans;

map Map;

struct node
{
    int x,y,v;
} a[N*N];

struct edge
{
    int x,y,v;
} dp[N];

int get_num(string s)//返回每个人对应结点
{
    if(Map.find(s)==Map.end())//没有搜索到该键值
    {
        Map[s]=++cnt;//对应建图
    }
    // cout<<"  Map["<G[x][i])//dp(v)=max(dp(father(v)),ω(father(v),v));
                {
                    dp[i]=dp[x];
                }
                else
                {
                    dp[i].v=G[x][i];
                    dp[i].x=x;
                    dp[i].y=i;
                }
            }
            dfs(i,x);
        }
}

void init()
{
    ans=0;
    cnt=1;
    Map["Park"]=1;
    memset(flag,0,sizeof(flag));
    memset(G,-1,sizeof(G));
    scanf("%d",&n);
    for(int i=1; i>s;
        a[i].x=get_num(s);
        cin>>s;
        a[i].y=get_num(s);
        cin>>a[i].v;
        if(G[a[i].x][a[i].y]==-1)
            G[a[i].x][a[i].y]=G[a[i].y][a[i].x]=a[i].v;
        else//有重边
            G[a[i].x][a[i].y]=G[a[i].y][a[i].x]=min(G[a[i].y][a[i].x],a[i].v);
    }
    scanf("%d",&m);//m表示限度值
}

void solve()
{
    int tmp[N],Min[N];
    for(int i=1; i<=cnt; i++)
        Min[i]=INF;
    sort(a+1,a+1+n,cmp);
    kruskal();
    for(int i=2; i<=cnt; i++)
    {
        if(G[1][i]!=-1)
        {
            int t=find_set(i);
            if(Min[t]>G[1][i])//求每个连通分量中和顶点1连接的最小权边
            {
                tmp[t]=i;
                Min[t]=G[1][i];
            }
        }
    }
    
    int t=0;//t表示最小限度
    for(int i=1; i<=cnt; i++)
        if(Min[i]!=INF)
        {
            t++;
            flag[1][tmp[i]]=flag[tmp[i]][1]=true;
            ans+=G[1][tmp[i]];
        }
        
    for(int i=t+1; i<=m; i++)//枚举t到m的所有最小生成树,即一步步将v1点的度加1,直到v1点的度为m为止;
    {
        memset(dp,-1,sizeof(dp));//dp[v]为路径v0—v上与v0无关联且权值最大的边;
        dp[1].v=-INF;
        for(int j=2; j<=cnt; j++)
            if(flag[1][j])
                dp[j].v=-INF;
        dfs(1,-1);
        int tmp,Min=INF;
        for(int j=2; j<=cnt; j++)
            if(G[1][j]!=-1)
            {
                if(Min>G[1][j]-dp[j].v)
                {
                    Min=G[1][j]-dp[j].v;
                    tmp=j;
                }
            }
        if(Min>=0)//找不到这样的边,就说明已经求出
            break;
        flag[1][tmp]=flag[tmp][1]=true;
        int x=dp[tmp].x;
        int y=dp[tmp].y;
        flag[x][y]=false;
        flag[y][x]=false;
        ans+=Min;
    }
    
    printf("Total miles driven: %d\n",ans);
}

int main()
{
    freopen("C:\\Users\\Administrator\\Desktop\\kd.txt","r",stdin);
    init();
    solve();
    return 0;
}

你可能感兴趣的:(算法分析,算法题解-图论,算法题解-图论-最小生成树)