您的位置:首页 > 其它

POJ 1639 Picnic Planning(有限制的最小生成树)

2017-08-23 23:56 399 查看
题目地址

题意:矮人虽小却喜欢乘坐巨大的轿车,轿车大到可以装下无论多少矮人。某天,N(N≤20)个矮人打算到野外聚餐。为了集中到聚餐地点,矮人A要么开车到矮人B家中,留下自己的轿车在矮人B家,然后乘坐B的轿车同行;要么直接开车到聚餐地点,并将车停放在聚餐地。 虽然矮人的家很大,可以停放无数量轿车,但是聚餐地点却最多只能停放K辆轿车。现在给你一张加权无向图,它描述了N个矮人的家和聚餐地点,要你求出所有矮人开车的最短总路程。

思路:其实就是有限制的最小生成树,主要的思想是把根节点先删除,使得树变成m棵子树,分别求m棵子树的最小生成树再求和,然后再找每棵生成树中与根节点相距最近的边,连上,这就成为了m度的最小生成树,然后再在各个生成树中找到可以与根节点能连接的节点,选最小的,然后因为这样加边就会导致成环,所以删除环中最长的边,这样依次下去就能构成k度最小生成树(因为该题只是不要大于k,所以如果权重不再减小就可以停止了)。

推荐:2004年国家集训队论文

#include <iostream>
#include <cstring>
#include <string>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <cstdio>
#include <algorithm>
#define N 50
#define M 90010
#define LL __int64
#define inf 0x3f3f3f3f
#define lson l,mid,ans<<1
#define rson mid+1,r,ans<<1|1
#define getMid (l+r)>>1
#define movel ans<<1
#define mover ans<<1|1
using namespace std;
const LL mod = 1e9 + 7;
map<string, int> ad;
int mapp

;
int len
, clo
, pre
, fst
, max_side
;
int n, m, k;
void init() {
ad.clear();
memset(mapp, inf, sizeof(mapp));
memset(len, inf, sizeof(len));//维护最小生成树的值
memset(clo, 0, sizeof(clo));//表明是哪个子树的
memset(pre, -1, sizeof(pre));//记录前驱
memset(fst, 0, sizeof(fst));//表明加上0->i这条边去与pack连接,让最小生成树与根节点相连
}
struct node {
int v, cap;
node() {}
node(int _v, int _cap) :v(_v), cap(_cap) {}
bool operator < (const node &a) const {
return a.cap<cap;
}
};
int Prim(int src, int id) {
priority_queue<node> q;
while (!q.empty())
q.pop();
len[src] = 0;
q.push(node(src, 0));
int ans = 0;
while (!q.empty()) {
node cur = q.top();
q.pop();
int u = cur.v;
if (!clo[u]) {
clo[u] = id;//标明是哪个子树的
ans += len[u];
for (int i = 1; i < n; i++) {
if (!clo[i] && len[i] > mapp[u][i]) {
pre[i] = u;
len[i] = mapp[u][i];
q.push(node(i, len[i]));
}
}
}
}
return ans;
}
void updata(int cur,int last,int maxside) {//更新两个点的最大距离
if (mapp[cur][last] == inf) {
max_side[cur] = maxside;
}
else {
max_side[cur] = max(maxside, mapp[cur][last]);
}
for (int i = 1; i < n; i++) {
if (i != last&&mapp[cur][i] != inf && (pre[cur] == i || pre[i] == cur)) {
updata(i, cur, max_side[cur]);
}
}
}
void solve() {
int sum, cnt;
sum = 0;
cnt = 1;
for(int i=1;i<n;i++){//去除了根节点park
if (!clo[i]) {//如果还没有被之前的最小生成树搜到就以他为子树的根节点
sum += Prim(i, cnt++);//cnt图中的连通子图个数,即最小生成树个数
}
}
for (int i = 1; i < n; i++) {
int id = clo[i];
if (mapp[0][i] != inf && (!fst[id] || mapp[0][i] < mapp[0][fst[id]])) {
fst[id] = i;//表明加上0->i这条边去与pack连接
}
}
for (int i = 1; i < cnt; i++) {
sum += mapp[0][fst[i]];
mapp[0][fst[i]] = mapp[fst[i]][0] = inf;//因为这些边是不能改变的所以把边删除掉
updata(fst[i], 0, 0);
}
k = k - cnt + 1;//剩下可以加的度
while (k--) {
int tmp = 0;
for (int i = 1; i <= n; i++) {
if (mapp[0][i] != inf && (tmp == 0 || max_side[tmp] - mapp[0][tmp] < max_side[i] - mapp[0][i])) {
tmp = i;
}
}
if (max_side[tmp] <= mapp[0][tmp]) {//没有办法再减小了
break;
}
sum -= (max_side[tmp] - mapp[0][tmp]);
mapp[0][tmp] = mapp[tmp][0] = inf;//加上去以后就会有环了,所以删除环中的最长边
int p = 0;
for (int i = tmp; pre[i] != -1; i = pre[i]) {
if (p == 0 || mapp[p][pre[p]] < mapp[i][pre[i]]) {//删除最长的边
p = i;
}
}
pre[p] = -1;
updata(tmp, 0, 0);
}
cout << "Total miles driven: " << sum << endl;
}
int main() {
cin.sync_with_stdio(false);
string stra, strb;
int a, b, c;
while (cin >> m) {
init();
ad["Park"] = 0;
n = 1;
for (int i = 0; i < m; i++) {
cin >> stra >> strb >> c;
if (ad.find(stra) == ad.end()) {
ad[stra] = n;
a = n++;
}
else {
a = ad[stra];
}
if (ad.find(strb) == ad.end()) {
ad[strb] = n;
b = n++;
}
else {
b = ad[strb];
}
mapp[a][b] = min(mapp[a][b], c);
mapp[b][a] = min(mapp[b][a], c);
}
cin >> k;
solve();
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: