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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章