1. 程式人生 > >藍書(演算法競賽進階指南)刷題記錄——POJ1639 Picnic Planning(度限制最小生成樹)

藍書(演算法競賽進階指南)刷題記錄——POJ1639 Picnic Planning(度限制最小生成樹)

題目:POJ1639.

題目大意:給定一張無向圖,求這張無向圖的最小生成樹,其中這棵最小生成樹滿足節點1的度小於等於s.

我們對於一張圖,先將點1去掉,剩下的聯通塊內的的最小生成樹都求出,然後我們在列舉與1關聯的邊,將所有聯通塊與1只連一條最小的邊,這樣我們就求出了一棵最小T度生成樹,其中T等於去掉點1後聯通塊數量.

我們求出最小T度生成樹之後,我們再求最小S度生成樹.注意,我們下面所說的最小k度指的是點1的度必須為k.

我們考慮最小k度生成樹如何轉移到最小k+1度生成樹.我們考慮列舉一條與1相連但沒有被選入最小生成樹的邊(1,x),然後我們會發現出現了一個環,其中這個環由一條非樹邊(1,x)和一條樹鏈(x,1)組成.

我們考慮暴力列舉樹鏈上的每一條,找到邊權最大的邊,看是否最優即可.

但是這樣做的時間複雜度最壞情況下為O(ElogE+sVE),我們考慮更加優秀的做法.

我們發現這個演算法的瓶頸在於列舉到一個點時需要在列舉至多O(E)級別條邊,那麼我們是否可以考慮優化這個過程.

我們考慮每一次轉移都先進行一個樹形DP,這個DP的狀態f[x]表示樹上的鏈(1,x)上的最大邊權,並且記錄這條邊的編號,方便刪除.

那麼時間複雜度為O(ElogE+s(V+E)).

由於時限寬鬆,這裡寫了鄰接矩陣寫法的dfs,若要達到上述那個更優的時間複雜度,需要寫一個雙向連結串列形式的鄰接表.

程式碼如下:

#include<iostream>
#include<cstdio>
#include<map>
#include<algorithm>
  using namespace std;
#define Abigail inline void
#define mt map<string,int>::iterator
typedef long long LL;
const int N=300,INF=(1<<29)-1;
map<string,int>q;
struct graph_side{
  int x,y,v;
  bool operator < (const graph_side p)const{return v<p.v;}
}e[N+9];
int m,n,s,ans;
int fa[N+9];
int tr[N+9][N+9],g[N+9][N+9];
struct node{
  int x,y,v;
}dp[N+9];
int get(int u){return u^fa[u]?fa[u]=get(fa[u]):u;}
int kruskal(){
  int cnt=0;
  for (int i=1;i<=n;i++) fa[i]=i;
  sort(e+1,e+1+m);
  for (int i=1;i<=m;i++){
    if (e[i].x==1||e[i].y==1) continue;
    int grx=get(e[i].x),gry=get(e[i].y);
    if (grx==gry) continue;
    fa[grx]=gry;
    tr[e[i].x][e[i].y]=tr[e[i].y][e[i].x]=1;
    ans+=e[i].v;
  }
  for (int i=1;i<=m;i++){
    if (e[i].x^1&&e[i].y^1) continue;
    int grx=get(e[i].x),gry=get(e[i].y);
    if (grx==gry) continue;
    cnt++;
    fa[grx]=gry;
    tr[e[i].x][e[i].y]=tr[e[i].y][e[i].x]=1;
    ans+=e[i].v;
  }
  return cnt;
}
void dfs(int k,int fa){
  for (int i=2;i<=n;i++)
    if (i^fa&&tr[k][i]){
      dp[i].v=g[k][i];dp[i].x=k;dp[i].y=i;
      if (dp[i].v<dp[k].v) dp[i]=dp[k];
      dfs(i,k);
    }
}
Abigail into(){
  string s1,s2;
  mt it;
  int v;
  for (int i=1;i<=N;i++)
    for (int j=1;j<=N;j++)
      g[i][j]=INF;
  scanf("%d",&m);
  q.insert(make_pair("Park",n=1));
  for (int i=1;i<=m;i++){
    cin>>s1>>s2;
    it=q.find(s1);
    if (it==q.end()){
      e[i].x=++n;
      q.insert(make_pair(s1,n));
    }else e[i].x=q[s1];
    it=q.find(s2);
    if (it==q.end()){
      e[i].y=++n;
      q.insert(make_pair(s2,n));
    }else e[i].y=q[s2];
    scanf("%d",&e[i].v);
    g[e[i].x][e[i].y]=g[e[i].y][e[i].x]=min(e[i].v,g[e[i].x][e[i].y]);
  }
  scanf("%d",&s);
}
Abigail work(){
  int cnt=kruskal();
  for (int i=cnt+1;i<=s;i++){
    dfs(1,0);
    int minn=INF,v=0;
    for (int j=2;j<=n;j++)
      if (!tr[1][j]&&minn>g[1][j]-dp[j].v) minn=g[1][j]-dp[j].v,v=j;
    if (minn>=0||!v) break;
    ans+=minn;
    tr[1][v]=tr[v][1]=1;
    tr[dp[v].x][dp[v].y]=tr[dp[v].y][dp[v].x]=0;
  }
}
Abigail outo(){
  printf("Total miles driven: %d\n",ans);
}
int main(){
  int T=1,_=0;
  while (T--){
    into();
    work();
    outo();
  }
  return 0>_<0;
}