POJ-1639 Picnic Planning 度数限制最小生成树

 解法参考的论文:https://wenku.baidu.com/view/8abefb175f0e7cd1842536aa.html

觉得网上的代码好像都是用邻接矩阵来实现的,觉得可能数据量大了会比较慢。于是自己写了一遍。

实现细节可以看代码:

#include<iostream>
#include<cstdio> 
#include<cstring>
#include<algorithm>
#include<map>
#include<vector>
using namespace std;
const int N=10000+10;
const int INF=0x3f3f3f3f;
int n,m,k,ans,fa[N];
struct edge{
    int x,y,z;
    bool used;
    edge() {}
    edge(int x,int y,int z) : x(x),y(y),z(z) { used=0; }
    bool operator < (const edge &rhs) const {
        return z<rhs.z;
    }
}e[N];
map<string,int> mp;
vector<int> G[N];

int getfa(int x) { return x==fa[x] ? x : fa[x]=getfa(fa[x]); } 

void kruskal() {
    sort(e+1,e+m+1);
    for (int i=1;i<=n;i++) fa[i]=i;
    for (int i=1;i<=m;i++) {
        int fx=getfa(e[i].x),fy=getfa(e[i].y);
        if (e[i].x==1 || e[i].y==1 || fx==fy) continue;
        fa[fy]=fa[fx];
        e[i].used=1;
        ans+=e[i].z;
    }
}

int Max[N];
void dfs(int x,int f) {
    for (int i=0;i<G[x].size();i++) {
        int y; if (e[G[x][i]].x==x) y=e[G[x][i]].y; else y=e[G[x][i]].x;
        if (!e[G[x][i]].used || y==f) continue;
        if (x!=1) //这个很重要:去掉的边必须和V0不相连(否则会使得根节点度数减少) 
        if (Max[x]==0 || e[G[x][i]].z>e[Max[x]].z) Max[y]=G[x][i];
            else Max[y]=Max[x]; 
        dfs(y,x);
    }
}

int cnt=0,Min[N];
void solve() {
    memset(Min,0,sizeof(Min));
    for (int i=0;i<G[1].size();i++) {  //把各个MST森林最小边连到根节点 变成一棵树 
        int ty;
        if (e[G[1][i]].x==1) ty=getfa(e[G[1][i]].y);
            else ty=getfa(e[G[1][i]].x);
        if (Min[ty]==0 || e[G[1][i]].z<e[Min[ty]].z) Min[ty]=G[1][i];     
    }
    for (int i=1;i<=n;i++)
        if (Min[i]) { cnt++; ans+=e[Min[i]].z; e[Min[i]].used=1; }
        
    for (int i=cnt+1;i<=k;i++) {  //拓展根节点度数 
        memset(Max,0,sizeof(Max));
        dfs(1,0);  //dfs找每个点到根节点的路径上的最长边 
        int minn=INF,New,Old;
        for (int j=0;j<G[1].size();j++)
            if (!e[G[1][j]].used) {
                int y; if (e[G[1][j]].x==1) y=e[G[1][j]].y; else y=e[G[1][j]].x;  
                if (Max[y]==0) continue;
                int tmp=e[G[1][j]].z-e[Max[y]].z;
                if (tmp<minn) {  //记录本次结点拓展最大的收益 
                    minn=tmp;
                    New=G[1][j]; Old=Max[y];
                }
            }
        if (minn>=0) break;
        ans+=minn;
        e[New].used=1; e[Old].used=0;  //替换最长边 
    }
} 

int main()
{
    cin>>m;
    string s1,s2; int x,y,z;
    n=1; mp["Park"]=1;
    for (int i=1;i<=m;i++) {
        cin>>s1>>s2>>z;
        if (!mp.count(s1)) mp[s1]=++n;
        if (!mp.count(s2)) mp[s2]=++n;
        x=mp[s1]; y=mp[s2];
        e[i]=edge(x,y,z);
    }
    cin>>k;
    
    kruskal();  //先忽略根节点做一次MST 得到MST森林 
    for (int i=1;i<=m;i++) { G[e[i].x].push_back(i); G[e[i].y].push_back(i); }
    solve();  //拓展根节点度数得到更小的MST 
    
    printf("Total miles driven: %d\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/clno1/p/10697295.html