最小k度限制產生樹

來源:互聯網
上載者:User
/*************************************************演算法引入:最小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<m時,問題無解;對每個連通分量求一次最小產生樹;對於每個連通分量V’,用一條與V0直接連接的最小的邊把它與V0點串連起來,使其整體成為一個產生樹;就得到了一個m度限制產生樹,即為最小m度限制產生樹;(2)由最小m度限制產生樹得到最小m+1度限制產生樹;串連和V0相鄰的點v,則可以知道一定會有一個環出現(因為原來是一個產生樹);只要找到這個環上的最大權邊(不能與v0點直接相連)並刪除,就可以得到一個m+1度限制產生樹;枚舉所有和V0相鄰點v,找到替換後,增加權值最小的一次替換(如果找不到這樣的邊,就說明已經求出);就可以求得m+1度限制產生樹;如果每添加一條邊,都需要對環上的邊一一枚舉,時間複雜度將比較高;用動態規劃解決;設dp(v)為路徑v0—v上與v0無關聯且權值最大的邊;定義father(v)為v的父結點,由此可以得到狀態轉移方程:dp(v)=max(dp(father(v)),ω(father(v),v));邊界條件為dp[v0]=-∞(因為每次尋找的是最大邊,所以-∞不會被考慮),dp[v’]=-∞|(v0,v’)∈E(T);(3)當dT(v0)=k時停止(即當V0的度為k的時候停止),但不一定k的時候最優;演算法實現:並查集+kruskal;首先,每個連通分量的的最小產生樹可以直接用一個迴圈,迴圈著Kruskal求出;這裡利用了聯通分量間的獨立性,對每個連通分量分別求最小產生樹,和放在一起求,毫不影響;而且kruskral演算法保證了各連通分量邊的有序性;找最小邊的時候,可以用動態規劃,也可以這麼做:先走一個迴圈,但我們需要逆過來加邊,將與v0關聯的所有邊從小到達排序;然後將各連通分量串連起來,利用並查集可以保證每個連通分量只有一條邊與v0相連;由於邊已經從小到達排序,故與每個連通分量相連的邊就是每個連通分量與v0相連中的最小邊;然後求m+1度的最小產生樹時,可以直接用DFS,最小產生樹要一直求到k度,然後從中找出一個最優值;演算法測試:PKU1639(Picnic Planning);題目大意:給出m條邊,每條邊有兩個端點和一個權值;求這個圖在滿足以下條件的情況下的最小產生樹;在所有點中,有一個特殊點Park,它在求得的最小產生樹中的度必須小於等於某個值;**************************************************/#include<iostream>#include<string>#include<cstdio>#include<map>#include<cstring>#include<algorithm>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<string,int> 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["<<s<<"]=="<<Map[s]<<endl;    return Map[s];}bool cmp(node a,node b){    return a.v<b.v;}int find_set(int x){    if(x!=set[x])        set[x]=find_set(set[x]);    return set[x];}inline void union_set(int x,int y){    set[y]=x;}void kruskal()//求m個連通分量的最小產生樹{    for(int i=1; i<=n; i++)    {        if(a[i].x==1||a[i].y==1)            continue;        int x=find_set(a[i].x);        int y=find_set(a[i].y);        if(x==y)            continue;        flag[a[i].x][a[i].y]=flag[a[i].y][a[i].x]=true;        set[y]=x;        ans+=a[i].v;    }}void dfs(int x,int fa){    for(int i=2; i<=cnt; i++)        if(i!=fa&&flag[x][i])        {            if(dp[i].v==-1)            {                if(dp[x].v>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<N; i++)//並查集初始化        set[i]=i;    string s;    for(int i=1; i<=n; i++)    {        cin>>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;}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.