最小生成树专题练习

CH6201 走廊泼水节

给定一颗N个节点的树,要求增加若干条边,把这棵树扩充为完全图,并满足图的唯一最小生成树仍然是这棵树。求增加的边的权值总和最小是多少。
数据范围:N<=6000,边权均为非负整数。



基本思路:把N-1条边按权值从小到大排序,一次扫描每条树边,执行kruskal(在每一步加一个操作)。

设当前扫描到的边(x,y,v),若x,y不在一个集合中,则合并Sx,Sy,代价是(v+1)(|Sx||Sy|-1)。这样就保证了每个集合都是一个完全图,(v+1)则保证了原树为图的最小生成树。

时间复杂度(n logn)。

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
    rg T data=0,w=1;rg char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-') w=-w;
    for(;isdigit(ch);ch=getchar()) data=data*10+ch-'0';
    return data*w;
}
template<class T>il T read(rg T&x) {return x=read<T>();}
typedef long long ll;
using namespace std;

co int N=6001;
struct rec {int x,y,z;}edge[N];
int fa[N],s[N],n;
ll ans;
bool operator<(co rec&a,co rec&b) {return a.z<b.z;}
int get(int x) {return fa[x]==x?x:fa[x]=get(fa[x]);}
int main(){
    for(int T=read<int>();T--;){
        read(n);
        for(int i=1;i<n;++i) read(edge[i].x),read(edge[i].y),read(edge[i].z);
        sort(edge+1,edge+n);
        for(int i=1;i<=n;++i) fa[i]=i,s[i]=1;
        ans=0;
        for(int i=1;i<n;++i){
            int x=get(edge[i].x),y=get(edge[i].y);
            if(x==y) continue;
            ans+=(ll)(edge[i].z+1)*(s[x]*s[y]-1);
            fa[x]=y,s[y]+=s[x];
        }
        printf("%lld\n",ans);
    }
    return 0;
}

POJ1639 Picnic Planning

转至accepoc的博客。

有n个兄弟去野餐,目的地为Park。每个人可以选择直接去Park,也可以选择去其他人家,和他一起坐车去Park。
每个人家的停车位没有限制,但是Park的停车数不能超过k。问所有人的最短路程。



假设Park的停车数没有限制,那么这题就是一道最小生成树了。
但是本题限制Park的停车数不能超过k,把Park看做根节点记为V0,那么就是说它的度数不能超过k。
得到一棵k度限制生成树的步骤:

  1. 忽略根节点做一次kruskal,此时得到的是一个森林,包含了m个最小生成树。
  2. 对于每一颗最小生成树树,选择其中离根节点最近的点,向根节点连一条边,此时得到了一棵m度的最小生成树。
  3. 由m度生成树得到m+1度生成树:
    (1). 用dp预处理出当前生成树中从V0到点i的路径上与V0无关联的权值最大的边,记为dp[i].d,该边的两端点记为dp[i].u和dp[i].v。
    (2). 对于一个不在生成树中的边<V0, v>, 如果将该边加入生成树中,则一定会得到一个环。
    此时我们删掉环中权值最大的边,即(1)中预处理得到的dp[v],得到一棵m+1度的生成树。
    (3). 对于(2)枚举每一个v,记minnum=min{(V0, v) - dist[v].d },使minnum得到最小值的点v就是这次选择的点。连接V0, v,删去dp[v]。
  4. 重复步骤3直到得到一棵k度限制生成树。本题要求度数不超过k,所以在某一步中,minnum>=0,就可以输出答案了。

关于minnum的含义:
minnum为在从m度生成树得到m+1度生成树的过程中,选择一个点v(连接V0, v,删去dp[v])可以得到的最大利益,即生成树的值最多可以减少多少。
minnum为负数,表示选择点v可以生成树的值减少,那么使minnum最小的点就是可以使生成树的值减少最多的点,这次我们便选择它。
如果minnum>=0,说明得到m+1度生成树不会获得任何利益,就不用继续下去,直接输出答案即可。

关于最小度数限制生成树详情参考2004国家集训队汪汀的论文。

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<map>
#include<vector>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
    rg T data=0,w=1;rg char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-') w=-w;
    for(;isdigit(ch);ch=getchar()) data=data*10+ch-'0';
    return data*w;
}
template<class T>il T read(rg T&x) {return x=read<T>();}
typedef long long ll;
using namespace std;

co int N=40,INF=0x3f3f3f3f;
struct E{
    int x,y,z;
    bool operator<(co E&w)co {return z<w.z;}
}f[N];
int n,k,tot,ans,a[N][N],fa[N],d[N],v[N];
map<string,int> m;
vector<E> e;
bool b[N][N];
int get(int x) {return fa[x]==x?x:fa[x]=get(fa[x]);}
void dfs(int x,int fa){
    for(int i=2;i<=tot;++i){
        if(i==fa||!b[x][i]) continue;
        if(f[i].z==-1){
            if(f[x].z>a[x][i]) f[i]=f[x];
            else f[i].x=x,f[i].y=i,f[i].z=a[x][i];
        }
        dfs(i,x);
    }
}
int main(){
    read(n);
    memset(a,0x3f,sizeof a);
    memset(d,0x3f,sizeof d);
    m["Park"]=tot=1;
    for(int i=0;i<N;++i) fa[i]=i;
    for(int i=1;i<=n;++i){
        E w;
        string s1,s2;
        cin>>s1>>s2>>w.z;
        w.x=m[s1]?m[s1]:(m[s1]=++tot);
        w.y=m[s2]?m[s2]:(m[s2]=++tot);
        e.push_back(w);
        a[w.x][w.y]=a[w.y][w.x]=min(a[w.x][w.y],w.z);
    }
    cin>>k;
    sort(e.begin(),e.end());
    for(unsigned i=0;i<e.size();++i){
        if(e[i].x==1||e[i].y==1) continue;
        int rtx=get(e[i].x),rty=get(e[i].y);
        if(rtx!=rty){
            fa[rtx]=rty;
            b[e[i].x][e[i].y]=b[e[i].y][e[i].x]=1;
            ans+=e[i].z;
        }
    }
    for(int i=2;i<=tot;++i)
        if(a[1][i]!=INF){
            int rt=get(i);
            if(d[rt]>a[1][i]) d[rt]=a[1][v[rt]=i]; // 每颗最小生成树中距离根节点最近的点与根节点的距离
        }
    for(int i=1;i<=tot;++i)
        if(d[i]!=INF){
            --k;
            b[1][v[i]]=b[v[i]][1]=1;
            ans+=a[1][v[i]];
        }
    while(k--){
        memset(f,-1,sizeof f);
        f[1].z=-INF;
        for(int i=2;i<=tot;++i)
            if(b[1][i]) f[i].z=-INF;
        dfs(1,-1); // dp预处理
        int o,w=-INF;
        for(int i=2;i<=tot;++i)
            if(w<f[i].z-a[1][i])
                w=f[i].z-a[1][o=i];
        if(w<=0) break;
        b[1][o]=b[o][1]=1;
        b[f[o].x][f[o].y]=b[f[o].y][f[o].x]=0;
        ans-=w;
    }
    printf("Total miles driven: %d\n",ans);
    return 0;
}

POJ2728 Desert King

有n个村庄,村庄在不同坐标和海拔,现在要对所有村庄供水,只要两个村庄之间有一条路即可,建造水管距离为坐标之间的欧几里德距离,费用为海拔之差,现在要求方案使得费用与距离的比值最小。



很显然,这个题目是要求一棵最优比率生成树。01分数规划问题,二分解决即可。
由于POJ这时炸了(502 Bad Gateway),所以这题代码就看别人的就行了。

扫描二维码关注公众号,回复: 6433788 查看本文章
//Author:XuHt
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1006, INF = 0x3f3f3f3f;
const double eps = 1e-6;
int n;
struct P {
    int x, y, z;
} p[N];
double a[N][N], b[N][N], c[N][N], d[N];
bool v[N];

inline double s(int i, int j) {
    return sqrt((p[i].x - p[j].x) * (p[i].x - p[j].x) + (p[i].y - p[j].y) * (p[i].y - p[j].y));
}

double work(double k) {
    for (int i = 1; i <= n; i++)
        for (int j = i; j <= n; j++)
            if (i == j) c[i][j] = INF;
            else c[i][j] = c[j][i] = a[i][j] - k * b[i][j];
    memset(v, 0, sizeof(v));
    for (int i = 1; i <= n; i++) d[i] = INF;
    d[1] = 0;
    double ans = 0;
    while (1) {
        int x = 0;
        for (int i = 1; i <= n; i++)
            if (!v[i] && (!x || d[x] > d[i])) x = i;
        if (!x) break;
        v[x] = 1;
        ans += d[x];
        for (int i = 1; i <= n; i++) d[i] = min(d[i], c[x][i]);
    }
    return ans;
}

void Desert_King() {
    for (int i = 1; i <= n; i++)
        scanf("%d %d %d", &p[i].x, &p[i].y, &p[i].z);
    double num = 0;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++) {
            num += (a[i][j] = a[j][i] = abs(p[i].z - p[j].z));
            b[i][j] = b[j][i] = s(i, j);
        }
    double l = 0, r = num;
    while (l + eps <= r) {
        double mid = (l + r) / 2;
        if (work(mid) >= 0) l = mid;
        else r = mid;
    }
    printf("%.3f\n", l);
}

int main() {
    while (cin >> n && n) Desert_King();
    return 0;
}

CH6202 黑暗城堡

题目要求源点到其余点的最短路径d[i],并且求树上路径s[i]等于d[i]的生成树
所以先求出最短路径d[i],并记录d[i[是哪个点的最短路径属性为v;
然后按长度大小进行排序,为了避免重复计数,,最后把满足d[x]=d[y]+w[i][j]的一个点一个点的往生成树之中加
根据乘法原理求出答案即可

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
    rg T data=0,w=1;rg char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-') w=-w;
    for(;isdigit(ch);ch=getchar()) data=data*10+ch-'0';
    return data*w;
}
template<class T>il T read(rg T&x) {return x=read<T>();}
typedef long long ll;
using namespace std;

co int N=1001;
int n,m,a[N][N],d[N];
bool v[N];
int main(){
    read(n),read(m);
    memset(a,0x3f,sizeof a);
    for(int i=1;i<=n;++i) a[i][i]=0;
    for(int i=1,x,y,z;i<=m;++i){
        read(x),read(y),read(z);
        a[x][y]=a[y][x]=min(a[x][y],z);
    }
    memset(d,0x3f,sizeof d);
    d[1]=0;
    for(int i=1;i<n;++i){
        int t=0;
        for(int j=1;j<=n;++j)
            if(!v[j]&&(!t||d[j]<d[t])) t=j;
        v[t]=1;
        for(int j=1;j<=n;++j)
            d[j]=min(d[j],d[t]+a[t][j]);
    }
    memset(v,0,sizeof v);
    v[1]=1;
    int ans=1;
    for(int i=1;i<=n;++i){
        int t=0,k=0;
        for(int j=2;j<=n;++j)
            if(!v[j]&&(!t||d[j]<d[t])) t=j;
        for(int j=1;j<=n;++j)
            if(v[j]&&d[j]+a[j][t]==d[t]) ++k;
        v[t]=1;
        ans=(ll)ans*k%0x7fffffff;
    }
    printf("%d\n",ans);
    return 0;
}

POJ2349 Arctic Network

Description

The Department of National Defence (DND) wishes to connect several northern outposts by a wireless network. Two different communication technologies are to be used in establishing the network: every outpost will have a radio transceiver and some outposts will in addition have a satellite channel.
Any two outposts with a satellite channel can communicate via the satellite, regardless of their location. Otherwise, two outposts can communicate by radio only if the distance between them does not exceed D, which depends of the power of the transceivers. Higher power yields higher D but costs more. Due to purchasing and maintenance considerations, the transceivers at the outposts must be identical; that is, the value of D is the same for every pair of outposts.

Your job is to determine the minimum D required for the transceivers. There must be at least one communication path (direct or indirect) between every pair of outposts.

Input

The first line of input contains N, the number of test cases. The first line of each test case contains 1 <= S <= 100, the number of satellite channels, and S < P <= 500, the number of outposts. P lines follow, giving the (x,y) coordinates of each outpost in km (coordinates are integers between 0 and 10,000).



直接求最小生成树。卫星用来连通最后剩下的连通块。

#include<iostream>
#include<cmath>
#include<algorithm>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
    rg T data=0,w=1;rg char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-') w=-w;
    for(;isdigit(ch);ch=getchar()) data=data*10+ch-'0';
    return data*w;
}
template<class T>il T read(rg T&x) {return x=read<T>();}
typedef long long ll;
using namespace std;

co int N=501;
pair<int,int> p[N];
struct E{
    int x,y,z;
    il bool operator<(co E&w)co{
        return z<w.z;
    }
}e[N*N];
int n,m,fa[N];

il int dis(int x,int y){
    return (p[x].first-p[y].first)*(p[x].first-p[y].first)+(p[x].second-p[y].second)*(p[x].second-p[y].second);
}
int get(int x) {return fa[x]==x?x:fa[x]=get(fa[x]);}
void Arctic_Network(){
    read(n),read(m);
    for(int i=1;i<=m;++i){
        fa[i]=i,read(p[i].first),read(p[i].second);
    }
    int tot=0;
    for(int i=1;i<=m;++i)
        for(int j=i+1;j<=m;++j)
             e[++tot].x=i,e[tot].y=j,e[tot].z=dis(i,j);
    sort(e+1,e+tot+1);
    int cnt=m-n;
    for(int i=1;i<=tot;++i){
        int x=get(e[i].x),y=get(e[i].y);
        if(x==y) continue;
        fa[x]=y;
        if(!--cnt) return printf("%.2lf\n",sqrt((double)e[i].z)),void();
    }
}
int main(){
    for(int t=read<int>();t--;) Arctic_Network();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/autoint/p/10914438.html