各种奇怪的生成树问题

各种奇怪的生成树问题

最近做了5道生成树问题,现在总结一下。

  • 求最大边最小边差值最小的生成树
  • 次小生成树
  • 有向图生成树(最小树形图)
  • 最优比率生成树
  • 顶点度数有限制的MST

1.求最大边最小边差值最小的生成树

题目

POJ3522

求一颗生成树,让最大边最小边差值最小,kruskal。
将边排序后,从最小的开始依次枚举。很暴力的做法,没想出更好的方法,百度了一下,差不多都是这个暴力的想法。

主要代码



const int maxn = 110;

struct Edge {
    int from,to,len;
};

int n , m;
int father[maxn];

Edge edge[maxn * maxn ];

bool cmp ( Edge a , Edge b ) {
    return a.len < b.len ;
}

int find ( int x ) {
    return ( x == father[x] ) ? x : father[x] = find (father[x] );
}

int main()
{
    while ( ~scanf("%d%d",&n,&m) && n+m ) {

        for ( int i = 0 ; i < m ; i++ ) {
            scanf("%d%d%d",&edge[i*2].from,&edge[i*2].to,&edge[i*2].len);
            edge[i*2+1].from = edge[i*2].to;
            edge[i*2+1].to = edge[i*2].from;
            edge[i*2+1].len = edge[i*2].len;
        }

        m *= 2;
        sort ( edge , edge + m , cmp );

        int st = 0 , ans = -1;
        while ( st + n - 2 < m ) {

            for ( int i = 1 ; i <= n ; i++ ) father[i] = i;

            int cnt = 0 , maxlen = 0 , flag = 0;
            for ( int i = st ; i < m ; i++ ) {
                if ( cnt == n - 1 ) {
                    flag = 1;
                    break;
                }
                Edge e = edge[i];
                int u = find(e.from);
                int v = find(e.to);
                if ( u != v ) {
                    maxlen = max ( ans , e.len );
                    cnt++;
                    father[u] = v;
                }
            }
            if ( flag )
            ans = (ans==-1) ? maxlen-edge[st].len : min ( ans , maxlen-edge[st].len);
            st++;
        }

        printf("%d\n",ans);
    }
    return 0;
}

2.次小生成树

题目

POJ1679

判断MST是否唯一。可以求次小生成树,然后判断次小和最小是否相等。当然,不用求出次小,在求次小的过程中,就可以判断出是否唯一。

方法有两种:

  • 先求出MST。然后枚举MST的每一条边,每次删掉一条边,求MST,这些MST中的最小值,就是次小生成树
  • 先求出MST。然后枚举非MST上的一条边,加上这条边一定会形成一个环,然后找到这个环上除了加上的这条边的最大边(就是这个边的两个端点在MST上的路径中的最大边),减去这个最大边,加上新边,就是一颗新树。所有新树中的最小值,就是次小生成树。最大边可以用BFS或DFS预处理。

我采用的是第二种,写的很挫。

主要代码


const int maxn = 110;

struct Edge {
    int id;
    int from , to , len;
    int flag;
    Edge() {flag = 0;}
    Edge( int from , int to , int len ) :
        from(from),to(to),len(len) { flag = 0; }
    bool operator < ( const Edge& rhs ) const {
        return len > rhs.len ;
    }
};

vector  edges;
vector  G[maxn];
int n,m;
int vis[maxn] , dis[maxn] , nxt[maxn] , nxtlen[maxn];
vector  mst[maxn];
int f[maxn][maxn];

void AddEdge ( int x , int y , int z ) {
    edges.pb( Edge(x,y,z) );
    int m = sz(edges);
    edges[m-1].id = m-1;
    edges[m-1].flag = 0;
    G[x].pb( sz(edges) - 1 );
}

void init() 
{
    scanf("%d%d",&n,&m);
    for ( int i = 1 ; i <= n ; i++ ) G[i].clear();
    for ( int i = 1 ; i <= n ; i++ ) mst[i].clear();
    edges.clear();
    for ( int i = 1 ; i <= m ; i++ ) {
        int x , y , z ;
        scanf("%d%d%d",&x,&y,&z);
        AddEdge ( x , y , z );
        AddEdge ( y , x , z );
    }
}

int MST() {
    priority_queue  q;
    int ans = 0;
    clr(vis);
    for ( int i = 0 ; i < sz(G[1]) ; i++ ) {
        Edge& e = edges[G[1][i]] ; 
        int v = e.to;
        if ( dis[v] > e.len ) {
            dis[v] = e.len;
            Edge ee;
            ee.from = 1; ee.to = v ; ee.len = dis[v] ; ee.id = e.id;
            q.push(ee);
        }
    }
    vis[1] = 1;
    while ( !q.empty() ) {
        Edge e = q.top(); q.pop();
        int u = e.to;
        if ( vis[u] ) continue;
        vis[u] = 1;
        ans += dis[u];
        e.flag = 1;

        mst[e.from].pb( mp ( e.to , e.len ) );
        mst[e.to].pb( mp ( e.from , e.len ));

        edges[e.id].flag = 1;
        edges[e.id^1].flag = 1;
        for ( int i = 0 ; i < sz ( G[u] ) ; i++ ) {
            Edge& e = edges[ G[u][i] ];
            if ( !vis[e.to] && dis[e.to] > e.len ) {
                dis[e.to] = e.len;
                Edge ee;
                ee.from = u ; ee.to = e.to; ee.len = e.len ; ee.id = e.id; 
                q.push(ee);
            }
        }
    }
    return ans;
}

void BFS ( int s ) {
    queue  q;
    clr(vis);
    q.push(s);
    vis[s] = 1;
    while ( !q.empty() ) {
        int u = q.front(); q.pop();
        for ( int i = 0 ; i < sz(mst[u]) ; i++ ) {
            int v = mst[u][i].first;
            f[s][v] = max ( f[s][u] , mst[u][i].second );
            if ( !vis[v] ) { q.push(v); vis[v] = 1; }
        }
    }
}

int main()
{
//    freopen("a.in","r",stdin);

    int T;
    scanf("%d",&T);
    while ( T-- ) {
        init();
        memset(nxt,-1,sizeof(nxt));
        memset(nxtlen,-1,sizeof(nxtlen));
        for ( int i = 1 ; i <= n ; i++ ) dis[i] = inf;
        int ans = MST();

        memset( f , -1 , sizeof(f) );

        for ( int i = 1 ; i <= n ; i++ ) BFS(i);

        for ( int i = 1 ; i <= n ; i++ ) 
            for ( int j = 1 ; j <= n ; j++ ) 
                if ( f[i][j] != -1 ) f[j][i] = f[i][j];



        int flag = 0;
        for ( int i = 0 ; i < sz(edges) ; i++ ) {
            Edge& e = edges[i];
            if ( e.flag == 0 && e.len == f[e.from][e.to] ) {
                flag = 1;
                break;
            }
        }
        if ( flag ) 
            printf("Not Unique!\n");
        else 
            printf("%d\n",ans);
    }
    return 0;
}

3.有向图生成树(最小树形图)

题目

POJ3164

最小树形图的模板题。我直接用了模板。
关于算法的讲解,请点这里

主要代码

  • 邻接矩阵

const int VN = 105;  
const int INF = 0x7fffffff;   
template  
class Directed_MST{  
public:  
    void init(int _n){  
        n=_n;   
        ans = 0;  
        memset(vis, 0, sizeof(vis));  
        memset(inc, 0, sizeof(inc));  
        for(int i=0; i <= n; ++i){  
            w[i][i] = INF;  
            for(int j=i+1; j<=n; ++j)  
                w[i][j]=w[j][i]=INF;  
        }  
    }  
    void insert(int u, int v, Type _w){  
        if(w[u][v]>_w) w[u][v] = _w;  
    }  
    Type directed_mst(int u){  
        //==  步骤1: 判断能否形成最小树形图,直接dfs遍历   
        dfs(u);  
        for(int i=1; i <= n; ++i)  
            if(!vis[i]) { return -1; }  

        //== 如果可以形成最小树形图,继续  
        memset(vis, 0, sizeof(vis));  
        while(true){  
            //== 1. 找最小前驱边    
            for(int i=1; i <= n; ++i)if(i!=u&&!inc[i]){  
                w[i][i]=INF, pre[i] = i;  
                for(int j=1; j<=n; ++j)if(!inc[j] && w[j][i]<w[pre[i]][i]){ pre[i]="j;" }="" =="2.判断是否有环" int="" i;="" for(i="1;" i="" <="n;" ++i)if(i!="u&&!inc[i]){" j="i," cnt="0;" while(j!="u" &&="" pre[j]!="i" ++cnt;="" if(j="=u" ||="">n) continue; //没找到  
                break;  
            }  

            //== 没有找到环,得到答案                
            if(i>n){    
                for(int i=1; i <= n; ++i)if(i!=u && !inc[i]) ans+=w[pre[i]][i];  
                return ans;  
            }  
            //==  有环,进行收缩    
            int j=i;  
            memset(vis, 0, sizeof(vis));  
            do{  
                ans += w[pre[j]][j], j=pre[j], vis[j]=inc[j]=true;  
            }while(j!=i);  
            inc[i] = false; // 环缩成了点i,点i仍然存在  

            //==  收缩  
            for(int k=1; k <= n; ++k)if(vis[k]){ // 在环中点点  
                for(int j=1; j <= n; ++j)if(!vis[j]){  // 不在环中的点  
                    if(w[i][j] > w[k][j]) w[i][j] = w[k][j];  
                    if(w[j][k] < INF && w[j][k]-w[pre[k]][k] < w[j][i])  
                        w[j][i] = w[j][k] - w[pre[k]][k];  
                }    
            }  
        }  
        return ans;        
    }  
private:  
    // 从根结点遍历一遍,判断是否存在最小树形图  
    void dfs(int u){  
        vis[u] = true;  
        for(int i=1; i <= n; ++i)if(!vis[i]&&w[u][i] < INF){  
            dfs(i);  
        }  
    }  
private:  
    Type ans;         // 所求答案  
    int n;            // 结点个数  
    int pre[VN];      // 权值最小的前驱边  
    bool vis[VN];     // 是在环中还是在环外  
    bool inc[VN];     // 该点是否被删除了(收缩)  
    Type w[VN][VN];   // 图  
};

  • 邻接表

struct node  
{  
    int u, v;  
    type w;  
}edge[MAXN * MAXN];  
int pre[MAXN], id[MAXN], vis[MAXN], n, m;  
type in[MAXN];    
type Directed_MST(int root, int V, int E)  
{  
    type ret = 0;  
    while(true)  
    {  
        //1.找最小入边  
        for(int i = 0; i < V; i++)  
           in[i] = INF;  
        for(int i = 0; i < E; i++)  
        {  
            int u = edge[i].u;  
            int v = edge[i].v;  
            if(edge[i].w < in[v] && u != v)  
              {pre[v] = u; in[v] = edge[i].w;}  
        }  
        for(int i = 0; i < V; i++)  
        {  
            if(i == root) continue;  
            if(in[i] == INF) return -1;//除了根以外有点没有入边,则根无法到达它  
        }  
        //2.找环  
        int cnt = 0;  
        memset(id, -1, sizeof(id));  
        memset(vis, -1, sizeof(vis));  
        in[root] = 0;  
        for(int i = 0; i < V; i++) //标记每个环  
        {  
            ret += in[i];  
            int 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] = cnt;  
                id[v] = cnt++;  
            }  
        }  
        if(cnt == 0) break; //无环   则break  
        for(int i = 0; i < V; i++)  
            if(id[i] == -1) id[i] = cnt++;  
        //3.建立新图  
        for(int i = 0; i < E; i++)  
        {  
            int u = edge[i].u;  
            int v = edge[i].v;  
            edge[i].u = id[u];  
            edge[i].v = id[v];  
            if(id[u] != id[v]) edge[i].w -= in[v];  
        }  
        V = cnt;  
        root = id[root];  
    }  
    return ret;  
} 

4.最优比率生成树

题目

POJ2728

求最优比例生成树。
关于分数规划,可以看论文 《最小割在信息学竞赛中的应用》 ,还有黑书
有两种方法,一种是二分,一种是Dinkelbach算法。
此题如果用二分,生成树要用O(n)的算法才不会超时,所以用Dinkelbach了。

主要代码

  • 二分 (TLE)

const int maxn = 1010;

struct Edge {
    int from , to;
    double b,c;
    double d;
    Edge(){}
    Edge( int u , int v , double b , double c ) :
        from(u) , to(v) , b(b) , c(c) {}
    bool operator < ( const Edge& rhs ) const {
        return d > rhs.d;
    }
};
int x[maxn] , y[maxn] , z[maxn];
vector  edges;
vector  G[maxn];
double dis[maxn];
int n;

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

void AddEdge ( int u  , int v ) {
    edges.pb( Edge( u , v , dist(u,v) , fabs(z[u]-z[v]) ) );
    int m = sz(edges);
    G[u].pb(m-1);
}

double MST ( double l ) {
    for ( int i = 0 ; i < sz(edges) ; i++ ) edges[i].d = edges[i].c - edges[i].b * l;
    priority_queue  q;
    double ans = 0;
    int vis[maxn];
    clr(vis);
    for ( int i = 0 ; i < sz ( G[1] ) ; i++ ) {
        Edge& e = edges[ G[1][i] ];
        int v = e.to;
        if ( dis[v] > e.d ) {
            dis[v] = e.d;
            Edge ee;
            ee.from = 1; ee.to = v ; ee.d = e.d;
            q.push(ee);
        }
    }
    vis[1] = 1;
    while ( !q.empty() ) {
        Edge e = q.top() ; q.pop();
        int u = e.to;
        if ( vis[u] ) continue;
        vis[u] = 1;
        ans += dis[u];
        for ( int i = 0 ; i < sz ( G[u] ) ; i++ ) {
            Edge& e = edges[ G[u][i] ] ;
            int v = e.to;
            if ( !vis[v] && dis[v] > e.d ) {
                dis[v] = e.d;
                Edge ee;
                ee.from = 1; ee.to = v ; ee.d = e.d;
                q.push(ee);
            }
        }
    }
    return ans;
}
int init() 
{
    scanf("%d",&n);
    if ( n == 0 ) return 0;
    for ( int i = 1 ; i <= n ; i++ ) scanf("%d%d%d",&x[i],&y[i],&z[i]) , G[i].clear();
    for ( int i = 1 ; i <= n ; i++ ) 
        for ( int j = 1 ; j <= n ; j++ ) 
            if ( i != j ) AddEdge ( i , j );
    return 1;
}

int main()
{
    while ( init() ) {

        double l = 0.0 , r = 10000.0;
        while ( l + eps < r ) {
            double mid = ( l + r ) / 2;
            for ( int i = 1 ; i <= n ; i++ ) dis[i] = inf;
            double ans = MST(mid);
            if ( ans > 0 ) l = mid;
            else r = mid;
        }
        printf("%.3lf\n",l);
    }
    return 0;
}
  • Dinkelbach (Memory: 52556 KB Time: 2407 MS 也是好悬的飘过啊)

const int maxn = 1010;

struct Edge {
    int from , to;
    double b,c;
    double d;
    Edge(){}
    Edge( int u , int v , double b , double c ) :
        from(u) , to(v) , b(b) , c(c) {}
    bool operator < ( const Edge& rhs ) const {
        return d > rhs.d;
    }
};
int x[maxn] , y[maxn] , z[maxn];
vector  edges;
vector  G[maxn];
double dis[maxn];
int n;

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

void AddEdge ( int u  , int v ) {
    edges.pb( Edge( u , v , dist(u,v) , fabs(z[u]-z[v]) ) );
    int m = sz(edges);
    G[u].pb(m-1);
}

double MST ( double l ) {
    for ( int i = 0 ; i < sz(edges) ; i++ ) edges[i].d = edges[i].c - edges[i].b * l;
    priority_queue  q;
    double ans = 0 , p = 0 , qq = 0;
    int vis[maxn];
    clr(vis);
    for ( int i = 0 ; i < sz ( G[1] ) ; i++ ) {
        Edge& e = edges[ G[1][i] ];
        int v = e.to;
        if ( dis[v] > e.d ) {
            dis[v] = e.d;
            Edge ee;
            ee.from = 1; ee.to = v ; ee.c = e.c ; ee.b = e.b; ee.d = e.d;
            q.push(ee);
        }
    }
    vis[1] = 1;
    while ( !q.empty() ) {
        Edge e = q.top() ; q.pop();
        int u = e.to;
        if ( vis[u] ) continue;
        vis[u] = 1;
        ans += dis[u];
        p += e.c;
        qq += e.b;
        for ( int i = 0 ; i < sz ( G[u] ) ; i++ ) {
            Edge& e = edges[ G[u][i] ] ;
            int v = e.to;
            if ( !vis[v] && dis[v] > e.d ) {
                dis[v] = e.d;
                Edge ee;
                ee.from = 1; ee.to = v ; ee.b =e.b; ee.c =e.c; ee.d = e.d;
                q.push(ee);
            }
        }
    }
    double ll = p/qq;
    if ( fabs ( l - ll ) > eps )  return ll;
    else {
       printf("%.3f\n",l);
       return 0;
    }
}
int init() 
{
    scanf("%d",&n);
    if ( n == 0 ) return 0;
    edges.clear();
    for ( int i = 1 ; i <= n ; i++ ) scanf("%d%d%d",&x[i],&y[i],&z[i]) , G[i].clear();
    for ( int i = 1 ; i <= n ; i++ ) 
        for ( int j = 1 ; j <= n ; j++ ) 
            if ( i != j ) AddEdge ( i , j );
    return 1;
}

int main()
{
    while ( init() ) {

        for ( int i = 1 ; i <= n ; i++ ) dis[i] = inf;
        double l = MST(0.0);
        while ( sgn(l) != 0 ) {
            for ( int i = 1 ; i <= n ; i++ ) dis[i] = inf;
            l = MST(l);
        }
    }
    return 0;
}

5.顶点度数有限制的MST

题目

POJ1639

资料可以看黑书,还有论文《最小生成树问题的拓展》

具体步骤如下:

  • 删除V0点,求最小生成森林,会得到 m 个连通分量。 求每个连通分量上距离V0最近的点。
  • 这样求得 m 度最小生成树 ,等于每个森林的和,再加上每个分量到V0的最近距离。
  • 对 生成树 进行 k-m 次 可行操作。 (若 k < m , 则无解)
  • 可行操作:枚举每一条V0出发而不在当前树上的边,记另一点为Vx。 找到所有符合条件的Vx中,length(V0,Vx) - maxcost(V0) 最小的 Vx。 连接V0和Vx ,删去maxcost的边。 得到新树,重复操作。
  • maxcost(Vx) 为Vx到V0在当前树的路径中的最大边。可以用DP预处理出来。

主要代码

这题现在还没AC。敲代码太不稳了。总是WA。

Author

  • Author : Praesidio
  • Time : 2014-8-19 22:24:47
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值