poj1639
有n个巨人要去Park聚会。巨人A和先到巨人B那里去,然后和巨人B一起去Park。B君是个土豪,他家的停车场很大,可以停很多车,但是Park的停车场是比较小。只能停k辆车。现在问你在这个限制条件下。居然到达Park的最短距离-------------------------------------------------------------------------------------分割线
如果把那个条件去掉。那么就是就是求最小生成树。加上那个条件其实就是求顶点度数限制为k的最小生成树。做法
1.忽略顶点V0。我们对其他顶点做一次最小生成树,此时形成一颗森林。
2.对步骤1中形成的每一颗最小生成树离顶点最近的点连起来。此时顶点的度数为m.
3.由m度->m+1度
(1)先dp预处理出当前生成树中与顶点V0到Vi的路径中与V0不关联的权值最大的边。dp[i].d记录的是权值,dp[i].u和dp[i].v记录的是边两边的点。
(2)对于每一个不在生成树的<v0,v>把他们加进去最小生成树中,那么久会生成一个环。我们把环中的最大的权值删去。即1中得到的dp[v].d。就会形成m+1个度
(3)对于(2)中我们枚举每一个顶点v。当minnum=min(g[v0][v]-dp[v].d)最小的时候。点v就是所求的点。 (4)重复步骤(3)直到k。其实当某一步的minnum>=0的时候就可以停止算法了。因为就算在增加度数生成树的权值也不会减小
#include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> #include<map> #include<set> #include<vector> using namespace std; const int INF=0x3f3f3f3f; const int maxn=22; struct Edge { int u,v,d; Edge (){} Edge(int a,int b,int c):u(a),v(b),d(c){} bool operator <(const Edge &e) const{ return d<e.d;} }; int n,m,k; int cnt; int ans; int parent[maxn]; map<string,int>nodes; vector<Edge>edges; int g[maxn][maxn]; bool tree[maxn][maxn]; int minEdge[maxn]; Edge dp[maxn]; int find(int p) { if(p==parent[p]) return parent[p]; return parent[p]=find(parent[p]); } void un(int p,int q) { parent[find(p)]=find(q); } void kruskal() { sort(edges.begin(),edges.end()); for(int i=0;i<edges.size();i++) { int p=edges[i].u; int q=edges[i].v; if(p==1||q==1) continue; if(find(p)!=find(q)) { un(p,q); tree[p][q]=tree[q][p]=1; ans+=edges[i].d; } } } void dfs(int cur,int pre) { for(int i=2;i<=cnt;i++) { if(i==pre||!tree[cur][i]) continue; if(dp[i].d==-1) { if(dp[cur].d>g[cur][i]) dp[i]=dp[cur]; else { dp[i].u=cur; dp[i].v=i; dp[i].d=g[cur][i]; } } dfs(i,cur); } } void slove() { int keyPoint[maxn]; //memset(keyPoint,0,sizeof(keyPoint)); for(int i=2;i<=cnt;i++) { if(g[1][i]!=INF) { int color=find(i); if(minEdge[color]>g[1][i]) { minEdge[color]=g[1][i]; keyPoint[color]=i; } } } for(int i=1;i<=cnt;i++) { if(minEdge[i]!=INF) { m++; tree[1][keyPoint[i]]=tree[keyPoint[i]][1]=1; ans+=g[1][keyPoint[i]]; } } for(int i=m+1;i<=k;i++) { memset(dp,-1,sizeof(dp)); dp[1].d=-INF; for(int j=2;j<=cnt;j++) if(tree[1][j]) dp[j].d=-INF; dfs(1,-1); int idx,minnum=INF; for(int j=2;j<=cnt;j++) { if(minnum>g[1][j]-dp[j].d) { minnum=g[1][j]-dp[j].d; idx=j; } } if(minnum>=0) break; tree[1][idx]=tree[idx][1]=1; tree[dp[idx].u][dp[idx].v]=tree[dp[idx].v][dp[idx].u]=1; ans+=minnum; } } void init() { memset(g,INF,sizeof(g)); memset(tree,0,sizeof(tree)); memset(minEdge,INF,sizeof(minEdge)); m=0; cnt=1; ans=0; nodes["Park"]=1; for(int i=0;i<maxn;i++) parent[i]=i; } int main() { scanf("%d",&n); string s1,s2; int d; init(); for(int i=1;i<=n;i++) { cin>>s1>>s2>>d; if(!nodes[s1]) nodes[s1]=++cnt; if(!nodes[s2]) nodes[s2]=++cnt; int u=nodes[s1]; int v=nodes[s2]; edges.push_back(Edge(u,v,d)); g[u][v]=g[v][u]=min(g[u][v],d); } scanf("%d",&k); kruskal(); slove(); printf("Total miles driven: %d\n",ans); return 0; }