您的位置:首页 > 其它

POJ 1639 Picnic Planning (k度限制最小生成树)

2014-09-21 22:33 393 查看
题目类型 k度限制最小生成树

题目意思
给出若干个点若干条边 其中某个点的度数限制最多为某个数 问此条件下原图的最小生成树是多少

解题方法

就是k度限制最小生成树的模型 参考 最小生成树问题的拓展

假设有限制的点为 x

1.把点x和与x关联的边去掉 然后原图剩下 y 个连通分量 求出这y个连通分量各自的最小生成树 然后每个连通分量用一条

 权值最小的边与x连起来 (这条边当然是这个连通分量内的点与x的边中权值最小的边) 这样就得出了 限制 y 度最小生成树 当然如果 k < y 则肯定

 不存在k度限制生成树

2.第1步求出了 y 度限制最小生成树 那么很容易就可以得出 y + 1 度限制最小生成树 方法是找一条与 x 相连的边假设是 (x, z) 那么如果在上一棵

 生成树上加入这条边的话显然会形成一个环 如果要消掉这个环就要删除一条边 那么删除上一棵最小生成树上(x,z)这条路径上权值最大的边就是

 最优的方法 (这里可以用一个dp数组保存点 x 到其他点的路径上权值最大的边, 转移方程是对于边 (u, v) dp[v] = max(dp[u], w[u][v]) )

那么加入边(x,z)后生成树的代价就变成 C = 原代价+w(x,z)-dp[z], 可以枚举所有的与x相连的边取 C 最小的方案, 然后把相应边加到生成树中再把应该要 删除的边删掉 然后再计算出一个新的 dp 数组 这样就得出了 y + 1度限制最小生成树 一直重复 k - y次即可 (如果过程中算出的最小的C比原来 生成树的代价还要大则直接结束)

参考代码 - 有疑问的地方在下方留言 看到会尽快回复的

很暴力的代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <map>
#include <vector>
#include <algorithm>

using namespace std;

const int maxn = 1000 + 10;
const int INF = 1<<29;

vector<int>E[maxn], W[maxn];
vector<int>cc[maxn];
int g[maxn][maxn], g2[maxn][maxn];
bool vis[maxn];
bool used[maxn];
int park, ncc, Nc[maxn];
int S[maxn];

struct Edge {
int u, v, w;
bool operator < (const Edge & rhs) const {
return w < rhs.w;
}
};

struct DP {
int nmax, u, v;
}dp[100][1000];

int fa[maxn], np;

void get_cc(int u) { // 找连通分量
vis[u] = true;
cc[ncc].push_back(u);
Nc[u] = ncc;
for( int i=0; i<E[u].size(); i++ ) {
int v = E[u][i];
if(v == park) continue;
if(vis[v]) continue;
get_cc(v);
}
}

int find(int x) {
return x == fa[x] ? x : find(fa[x]);
}

int kruscal(int x) { // 求某个连通分量的最小生成树
int no[maxn];
Edge e[maxn];
int ne = 0;
for( int i=0; i<cc[x].size(); i++ ) fa[i] = i;
for( int i=0; i<cc[x].size(); i++ ) {
fa[i] = i, no[i] = cc[x][i];
for( int j=i+1; j<cc[x].size(); j++ ) {
int u = cc[x][i], v = cc[x][j];
int w = g[u][v];
if(g[u][v] == -1) continue;
e[ne].u = i, e[ne].v = j, e[ne].w = w; ne++;
}
}
sort(e, e+ne);
int sum = 0;
int tn = cc[x].size();
for( int i=0; i<ne; i++ ) {
int u = e[i].u, v = e[i].v, w = e[i].w;
int xx = find(u), y = find(v);
if(xx != y) {
fa[xx] = y;
g2[cc[x][u]][cc[x][v]] = g2[cc[x][v]][cc[x][u]] = w;
//printf("g2[%d][%d] = %d\n", cc[x][u], cc[x][v], g2[cc[x][u]][cc[x][v]]);
sum += w;
tn--;
if(tn == 1) break;
}
}
//printf("sum =%d\n", sum);
return sum;
}

void find_dp(int u, int fa, int nx) { // 求出那个 dp 数组
vis[u] = true;
for( int i=1; i<np; i++ ) {
if(g2[u][i] == -1) continue;
int v = i;
if(v == fa) continue;
if(vis[v]) continue;
if(dp[nx][u].nmax > g2[u][v]) {
dp[nx][v].nmax = dp[nx][u].nmax;
dp[nx][v].u = dp[nx][u].u;
dp[nx][v].v = dp[nx][u].v;
}
else {
dp[nx][v].nmax = g2[u][v];
dp[nx][v].u = u;
dp[nx][v].v = v;
}
find_dp(v, u, nx);
}
}

void getEdge(int m) { // 根据输入的信息建图
map<string,int>M;
memset(g, -1, sizeof(g));
char lname[100], rname[100];
for( int i=0; i<=1000; i++ ) E[i].clear(), W[i].clear(), cc[i].clear();
for( int i=0; i<m; i++ ) {
int u, v, w;
scanf("%s%s%d", lname, rname, &w);
string s = lname;
if(M[s] == 0) { u = np; M[s] = np++; }
else u = M[s];
s = rname;
if(M[s] == 0) { v = np; M[s] = np++; }
else v = M[s];
if(strcmp(lname, "Park") == 0) park = u;
else if(strcmp(rname, "Park") == 0) park = v;
E[u].push_back(v);
W[u].push_back(w);
E[v].push_back(u);
W[v].push_back(w);
g[u][v] = g[v][u] = w;
//printf("u = %d v = %d w = %d\n", u, v, w);
}
}

int main() {
freopen("in", "r", stdin);
int m;
while(scanf("%d", &m) != EOF) {
np = 1;
getEdge(m);
int k;
scanf("%d", &k);
ncc = 0;
memset(vis, 0, sizeof(vis));
for( int i=1; i<np; i++ ) {
if(i == park) continue;
if(vis[i]) continue;
get_cc(i);
ncc++;
}
int sum = 0;
memset(used, 0, sizeof(used));
memset(dp, 0, sizeof(dp));
memset(g2, -1, sizeof(g2));
for( int i=0; i<ncc; i++ ) {
sum += kruscal(i);
int nmin = INF, mark;
for( int j=0; j<cc[i].size(); j++ ) {
int u = cc[i][j];
if(g[u][park] == -1) continue;
if(nmin > g[u][park]) {
nmin = g[u][park];
mark = u;
}
}
dp[i][mark].nmax = 0;
memset(vis, 0, sizeof(vis));
find_dp(mark, -1, i);
S[i] = mark;
used[mark] = true;
sum += nmin;
}
k -= ncc;
while(k--) {
int nmin = INF, mark, mu, mv;
for( int i=0; i<E[park].size(); i++ ) {
int v = E[park][i];
if(used[v]) continue;
int tmp = g[park][v] - dp[Nc[v]][v].nmax;
if(tmp < nmin) {
nmin = tmp;
mark = v;
mu = dp[Nc[v]][v].u, mv = dp[Nc[v]][v].v;
}
}
if(nmin >= 0) break;
sum += nmin;
used[mark] = true;
g2[mu][mv] = 0;
//for( int i=2; i<=8; i++ ) printf("%d ", dp[0][i].nmax);
memset(dp, 0, sizeof(dp));
for( int i=0; i<ncc; i++ ) {
memset(vis, 0, sizeof(vis));
find_dp(S[i], -1, i);
}
}
printf("Total miles driven: %d\n", sum);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: