[最小k度限制生成树] POJ 1639 Picnic Planning

论文:汪汀--最小生成树问题的拓展

另见:http://blog.csdn.net/jarily/article/details/8779621 http://www.cnblogs.com/ylfdrib/archive/2010/08/21/1805505.html

主要思想:先求出最小m度限制生成树 由最小m度限制生成树找环得到最小m+1度限制生成树

实现么 Prime/Kruskal dfs/DP 皆可


#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<map>
#include<string>
#include<iostream>
#include<cstring>
#define cl(x) memset(x,0,sizeof(x))
using namespace std;

const int N=105;

struct edges{
  int u,v,w,idx;
  edges(int u=0,int v=0,int w=0,int idx=0):u(u),v(v),w(w),idx(idx){ }
  bool operator < (const edges &B) const{
    return w<B.w;
  }
}Ed[N*N*2];

map<string,int> Map;

int n,m;

inline int Num(string s){
  if (Map.find(s)!=Map.end()) return Map[s];
  Map[s]=++n; return n;
}

namespace Tset{
  int fat[N];
  inline void init(int n){
    for (int i=1;i<=n;i++) fat[i]=i;
  }
  inline int Fat(int u){
    return u==fat[u]?u:fat[u]=Fat(fat[u]);
  }
  inline bool Merge(int x,int y){
    x=Fat(x); y=Fat(y); if (x==y) return 0;
    fat[x]=y; return 1;
  }
}

struct edge{
  int u,v,w,next;
}G[N*N*2];
int head[N],inum=1;
int tag[N*N*2];

inline void add(int u,int v,int w,int p){
  G[p].u=u; G[p].v=v; G[p].w=w; G[p].next=head[u]; head[u]=p;
}

int T,K;

int f[N];

#define V G[p].v
inline void dfs(int u,int fa){
  for (int p=head[u];p;p=G[p].next)
    if (V!=fa && tag[p]){
      if (u!=1)
	if (f[u]==-1 || G[p].w>G[f[u]].w)
	  f[V]=p;
	else
	  f[V]=f[u];
      dfs(V,u);
    }
}

int Ans=0;

int main(){
  using namespace Tset;
  string su,sv; int iu,iv,iw;
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  while (~scanf("%d",&m)){
    Map["Park"]=n=1;
    for (int i=1;i<=m;i++){
      cin>>su>>sv>>iw; iu=Num(su); iv=Num(sv);
      add(iu,iv,iw,++inum); add(iv,iu,iw,++inum);
      Ed[i]=edges(iu,iv,iw,i);
    }
    cin>>K;
    sort(Ed+1,Ed+m+1); init(n);
    for (int i=1;i<=m;i++){
      if (Ed[i].u==1 || Ed[i].v==1) continue;
      if (Merge(Ed[i].u,Ed[i].v))
	tag[Ed[i].idx<<1]=tag[Ed[i].idx<<1|1]=1,Ans+=Ed[i].w;
    }
    for (int i=1;i<=m;i++){
      if (!(Ed[i].u==1 || Ed[i].v==1)) continue;
      if (Merge(Ed[i].u,Ed[i].v))
	tag[Ed[i].idx<<1]=tag[Ed[i].idx<<1|1]=1,++T,Ans+=Ed[i].w;
    }
    for (int i=T+1;i<=K;i++){
      memset(f,-1,sizeof(f));
      dfs(1,0);
      int t,r,maxv=-1<<30;
      for (int p=head[1];p;p=G[p].next)
	if (!tag[p] && f[V]!=-1 && maxv<G[f[V]].w-G[p].w)
	  maxv=G[f[V]].w-G[p].w,t=f[V],r=p;
      if (maxv<=0) break;
      Ans-=maxv;
      tag[r]=tag[r^1]=1; tag[t]=tag[t^1]=0;
    }
    printf("Total miles driven: %d\n",Ans);
    cl(head); inum=1; cl(tag); T=0; Ans=0; Map.clear();
  }
  return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值