kuangbin专题8 生成树 次小生成树部分 HDU4081/UVA10600/UVA10462

前言

本来壮志凌云的想都做完 发现我在做梦。。。
朱刘算法太难了(自己太懒发现性价比比较低之后就没做而且算法介绍也太难懂了好几个关键词含义都不给简直简直太难了我枯

HDU4081 Qin Shi Huang’s National Road System

题意:给你一个图的各个点的座标 再给你每个点的权值。
题目是 求最小生成树的基础上 求A/B的最大值
现在来解释A和B是什么玩应
先求出最小生成树!
然后 在所有的边中 我们可以选择一条边 ij
A就是所选这条边所连两点的权值和
我们可以免费造这条边 所以B就是除去这条边其他的边的权值和
思路!!!:
用prim求解最小生成树 的同时!!!:求出两点间成环之后除去直接相连的这条边环上其他小边最长的那条!!!存到dp ij里 然后还要用pre数组记录某个点的父节点 这个pre数组用于方便记录某条边ij是否在最小生成树上(used ij)
方法具体看代码 一定好好琢磨!!!
然后遍历所有边!不只是树上的边

//sum是最小生成树权值  A是选择的边的两端点权值和 dpij解释如上
//对于最小生成树上的边
ans=min(ans,A/(sum-g[i][j]));
//对于不是最小生成树上的边
ans=min(ans,A/(sum-dp[i][j]);

下面是AC代码:

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
#define inf 0x3f3f3f3f
#define sd(a) scanf("%d",&a)
#define sdd(a,b) scanf("%d%d",&a,&b)
#define cl(a,b) memset(a,b,sizeof(a))
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define sddd(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define dbg() printf("aaa\n")
using namespace std;
//次小生成树emmm
//多一些步骤
const int maxn=1010;
int n;
struct node{
    int x,y,num;//座标 人数
}a[maxn];
double g[maxn][maxn];//存距离
double dis[maxn];//prim用
bool vis[maxn];//prim用
int pre[maxn];//存某一个结点的父结点是什么
bool used[maxn][maxn];//记录这条边有没有用过
double dp[maxn][maxn];//记录连接这两个点的环上
double prim(){
    rep(i,1,n){
        dis[i]=g[1][i];
        vis[i]=false;
        pre[i]=1;//从1开始搜嘛 就默认刚开始每个都可以被1搜到
        dp[i][i]=0.0;
        rep(j,i+1,n){//环上距离为0
            dp[i][j]=dp[j][i]=0.0;
        }
        rep(j,1,n) used[i][j]=false;
    }
    vis[1]=true;
    double ans=0.0;
    rep(i,1,n-1){
        double minn=1e18;
        int p;
        rep(j,1,n){
            if(!vis[j]&&minn>dis[j]){
                minn=dis[j];
                p=j;
            }
        }
        used[p][pre[p]]=used[pre[p]][p]=true;//标记这条边用过了
        vis[p]=true;
        rep(j,1,n){
            if(!vis[j]){
                if(dis[j]>g[p][j]){
                    dis[j]=g[p][j];
                    pre[j]=p;//你忘了大括号!!!
                }
            }else{
                if(j!=p){//对于1 2 3 在第3个搜寻时 若搜到1 则看minn(2 3(谁让它入队的))大还是1 2大
                    dp[j][p]=dp[p][j]=max(minn,dp[j][pre[p]]);//妙啊!!!
                }
            }
        }
        ans+=minn;
    }
    return ans;
}
int main() {
	int T;
    sd(T);
    while(T--){
        sd(n);
        rep(i,1,n){
            sddd(a[i].x,a[i].y,a[i].num);
        }
        rep(i,1,n){
            g[i][i]=0.0;
            rep(j,i+1,n){
                double dx=fabs((double)(a[i].x-a[j].x));
                double dy=fabs((double)(a[i].y-a[j].y));
                double len=sqrt((double)(dx*dx+dy*dy));
                g[i][j]=g[j][i]=len;
            }
        }
        double sum=prim();//已求出最小生成树
        double ans=0.0;
        rep(i,1,n){
            rep(j,i+1,n){//枚举每一条边
                double now;
                if(used[i][j]){//在树上
                    now=(a[i].num+a[j].num)/(sum-g[i][j]);
                }else{
                    now=(a[i].num+a[j].num)/(sum-dp[i][j]);
                }
                if(ans<now) ans=now;
            }
        }
        printf("%.2lf\n",ans);
    }
	return 0;
}
/*
4
4
2 3 9
5 0 3
0 3 7
1 5 4
*/

UVA10600 ACM Contest and Blackout

这题就是求最小和次小生成树板子题
用前面那题方法行 复杂度nn
n遍kruscal也行 复杂度n
m
下面是两种方法代码

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
#define inf 0x3f3f3f3f
#define sd(a) scanf("%d",&a)
#define sdd(a,b) scanf("%d%d",&a,&b)
#define cl(a,b) memset(a,b,sizeof(a	
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define sddd(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define dbg() printf("aaa\n")
using namespace std;
//求最小的两个生成树
//先用今天新学的方法
const int maxn=110;
int n,m;
int g[maxn][maxn];//存图
int dis[maxn];
bool vis[maxn];//prim用
int pre[maxn];
int dp[maxn][maxn];//除了ij这条边 环上最大的边是多长
bool used[maxn][maxn];
int prim(){
    rep(i,1,n){
        dis[i]=g[1][i];
        vis[i]=false;
        pre[i]=1;
        rep(j,i,n){
            dp[i][j]=dp[j][i]=0;
            used[i][j]=used[j][i]=false;
        }
    }
    vis[1]=true;
    int ans=0;
    rep(i,1,n-1){
        int minn=inf,p;
        rep(j,1,n){
            if(!vis[j]&&minn>dis[j]){
                minn=dis[j];
                p=j;
            }
        }
        vis[p]=true;
        used[p][pre[p]]=used[pre[p]][p]=true;
        ans+=minn;
        rep(j,1,n){
            if(!vis[j]){//若没有标记过
                if(dis[j]>g[p][j]){
                    dis[j]=g[p][j];
                    pre[j]=p;
                }
            }else{//若标记过
                if(j!=p){
                    dp[j][p]=dp[p][j]=max(minn,dp[j][pre[p]]);
                }
            }
        }
    }
    return ans;
}
int main() {
	int T;
    sd(T);
    while(T--){
        sdd(n,m);
        rep(i,1,n){
            rep(j,i,n){
                if(j==i) g[i][j]=0;
                else g[i][j]=g[j][i]=inf;
            }
        }
        rep(i,1,m){
            int u,v,val;
            sddd(u,v,val);
            g[u][v]=g[v][u]=val;
        }
        int ans=prim();
        int ans1=inf;//次小值
        rep(i,1,n){
            rep(j,i+1,n){
                if(!used[i][j]){//若原来不在树上
                    if(g[i][j]!=inf){
                        int now=ans+g[i][j]-dp[i][j];
                        if(ans1>now) ans1=now;
                    }
                }
            }
        }
        printf("%d %d\n",ans,ans1);
    }
	return 0;
}

下面是好多遍kruscal

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
#define inf 0x3f3f3f3f
#define sd(a) scanf("%d",&a)
#define sdd(a,b) scanf("%d%d",&a,&b)
#define cl(a,b) memset(a,b,sizeof(a	
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define sddd(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define dbg() printf("aaa\n")
using namespace std;
//求最小的两个生成树 这里用个老的方法 n遍kruscal
const int maxn=110;
int n,m;
int pre[maxn];
int rec[maxn];
struct Side{
    int u,v,val;
    bool operator<(const Side &oth)const{
        return val<oth.val;
    }
}a[maxn*maxn];//ans记录是哪条边
int find(int x){
    if(x==pre[x]) return x;
    return pre[x]=find(pre[x]);
}
void join(int a,int b){
    int fa=find(a),fb=find(b);
    pre[fa]=fb;
    return;
}
int kruscal(int id){
    rep(i,1,n) pre[i]=i;
    int ans=0;
    int cnt=0;
    for(int i=1;i<=m;i++){
        if(i==id) continue;
        int fa=find(a[i].u),fb=find(a[i].v);
        if(fa!=fb){
            join(a[i].u,a[i].v);
            cnt++;
            ans+=a[i].val;
        }
        if(cnt==n-1) break;
    }
    if(cnt==n-1) return ans;
    return -1;
}
int main() {
	int T;
    sd(T);
    while(T--){
        sdd(n,m);
        rep(i,1,n) pre[i]=i;
        rep(i,1,m){
            sddd(a[i].u,a[i].v,a[i].val);
        }
        sort(a+1,a+m+1);
        int cnt=0;
        int ans=0;
        rep(i,1,m){
            int fa=find(a[i].u),fb=find(a[i].v);
            if(fa!=fb){
                join(a[i].u,a[i].v);
                cnt++;
                rec[cnt]=i;
                ans+=a[i].val;
            }
            if(cnt==n-1) break;
        }
        int ans1=inf;
        rep(i,1,n-1){//枚举删除不同的边
            int now=kruscal(rec[i]);
            if(now==-1) continue;//这条边不能删除
            if(ans1>now) ans1=now;
        }
        printf("%d %d\n",ans,ans1);
    }
	return 0;
}

UVA10462

最后这题很迷 第一遍咋也不过
第二遍就过了。。。
这题只能用kruscal 因为给的边的关系 用点有点麻烦 而且边很少 只有2^n

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
#define inf 0x3f3f3f3f
#define sd(a) scanf("%d",&a)
#define sdd(a,b) scanf("%d%d",&a,&b)
#define cl(a,b) memset(a,b,sizeof(a	
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define sddd(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define dbg() printf("aaa\n")
using namespace std;
const int maxn=110;
int n,m;
struct Side{
    int u,v,val;
    bool operator<(const Side &oth)const{
        return val<oth.val;
    }
}side[maxn*2];
int pre[maxn],rec[maxn];
int find(int x){
    if(x==pre[x]) return x;
    return pre[x]=find(pre[x]);
}
int kruscal(int id){
    int cnt=0,ans=0;
    rep(i,1,n) pre[i]=i;
    rep(i,1,m){
        if(i==id) continue;
        int fa=find(side[i].u),fb=find(side[i].v);
        if(fa!=fb){
            pre[fa]=fb;
            cnt++;
            if(id==-1) rec[cnt]=i;
            ans+=side[i].val;
            if(cnt==n-1) break;
        }
    }
    if(cnt==n-1) return ans;
    return -1;//没找到合适的路
}
int main() {
	int T;
    sd(T);
    rep(cas,1,T){
        sdd(n,m);
        rep(i,1,m){
            sddd(side[i].u,side[i].v,side[i].val);
        }
        sort(side+1,side+m+1);
        int ans=kruscal(-1);
        int ans1=inf;
        rep(i,1,n-1){
            int now=kruscal(rec[i]);
            if(now!=-1){
                if(ans1>now) ans1=now;
            }
        }
        printf("Case #%d : ",cas);
        if(ans==-1) printf("No way\n");
        else{
            if(ans1==inf) printf("No second way\n");
            else printf("%d\n",ans1);
        }
    }
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章