在一棵生成树中,某个顶点v0的度数<=k称作度限制条件,把满足这一条件的生成树称为度限制生成树,把权值和最小的度限制生成树称为最小度限制生成树。
如果撇开度限制条件,那么就是最小生成树问题。首先,避开度限制条件。假如把最小度限制生成树中所有与v0相关的边都删掉,得到m个连通分量。
具体步骤:
1. 如果k<m,显然无解。
2. 求最小m度限制生成树。
3. 有最小m度限制生成树求最小m+1度限制生成树。
4. 当dT(v0)==k时,停止迭代。
说明:
求最小m度限制生成树时:去掉图中和v0相连的所有边,得到m个连通分量,而这m个连通分量必须通过v0来连接。所以在生成树中,dT(v0)>=m,如果k<m,问题无解。对每个连通分量求最小生成树。对于每个连通分量v',找到和v0相连的最小边。于是,我们得到了一个最小m度限制生成树。
由最小m度限制生成树得到最小m+1度限制生成树:对于和v0相邻的点v,添加后一定会形成一个环,找到环上权值最大的边,用(v0,v)替换掉。枚举所有和v0相邻的点,找到权值增加最小的一次替换,就可以得到最小m+1度限制生成树。每次枚举的时候都需要找换上不和v0相连的最大边,需要用到动态规划:设best[v]是v0到v路劲上不与v0相连的最大边,定义father[v]是v的父节点,状态转移方程为:
best[v] = max{best[father[v]], w(father[v],v)}
边界条件为best[v0]=INF,best[v']=INF((v0,v')是图中的边)。
模型与例题:
1. 考虑下面这个问题:
某地区有n个村庄,左边为(x,y)。现在村庄要建立通讯网络,安置卫星的村庄可以相互通信,铺设线路的村庄也可以相互通讯。卫星的分配是不受限制的。
问怎样合理的分配卫星和铺设线路,使得任意两个村庄都能直接或间接通信,并且铺设线路最短,求最短的线路是多少。n,k<=5000
解:如果不设卫星,就相当于求原图的最小生成树。如何改造该模型?增设一个点v0,和所有村庄相连,权值为0,该图的一个最小生成树就对应着一种方案,铺设路线长度为对应的生成树的权值之和,生成树中与v0相关的村庄为安放卫星的村庄。这样问题转化为求dT(v0)==k的最小生成树问题了。
2. POJ1639
题意:一些人准备去公园开party,每个人可以开车直接去或者把车开到a家,然后做a的车到公园,每辆车可以座无数个人,但公园最多只能停放k辆车。问所有人开车的路程之和最短为多少。
解:把公园点v0看成度限制条件,求所有<=k的度限制生成树,取最小值即可。
/*
最小度限制生成树
*/
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int INF = 0x7fffffff;
const int N = 30;
int n,S,k; //节点总数 有度数限制的点v0 度数限制为k
int mst; //最终结果:最小k限制度生成树
int mp[N][N]; //图
int father[N]; //节点n的父节点
bool edge[N][N]; //判断边(i,j)是否加入到生成树中
int best[N]; //从v0到v路径上与v0无关的最大权边的点序号
char str[N][12];
int dis[N];
bool mark[N];
bool vis[N];
int pre[N];
void dfs(int now)
{
for(int i = 0; i < n; i++)
{
if(edge[i][now] && mark[i])
{
father[i] = now;
mark[i] = false;
dfs(i);
}
}
}
int prim(int s) //从点s开始的最小生成树
{
int i,Min,key;
int sum = 0;
memset(pre,0,sizeof(pre));
memset(mark,0,sizeof(mark));
mark[s] = true;
vis[s] = true;
for(i = 0; i < n; i++)
{
dis[i] = mp[s][i];
pre[i] = s;
}
while(true)
{
Min = INF;
for(i = 0; i < n; i++)
{
if(!vis[i] && !mark[i] && dis[i] < Min)
{
Min = dis[i];
key = i;
}
}
if(Min == INF) break;
mark[key] = true;
vis[key] = true;
edge[pre[key]][key] = edge[key][pre[key]] = true;
sum += Min;
for(i = 0; i < n; i++)
{
if(!vis[i] && !mark[i] && dis[i] > mp[key][i])
{
dis[i] = mp[key][i];
pre[i] = key;
}
}
}
Min = INF;
int root = -1; //找到与v0相关联的点的最小边
for(i = 0; i < n; i++)
{
if(mark[i] && mp[i][S] < Min)
{
Min = mp[i][S];
root = i;
}
}
mark[root] = false;
dfs(root); // 将树拉成有根树
father[root] = S;
return sum + Min;
}
int Best(int x) //记忆化搜索s到x的最大权值的边
{
int tmpt;
if(father[x] == S)
return -1;
if(best[x] != -1)
return best[x];
tmpt = Best(father[x]);
if(tmpt != -1 && mp[tmpt][father[tmpt]] > mp[father[x]][x])
best[x] = tmpt;
else
best[x] = x;
return best[x];
}
int find(char *c)
{
for(int i = 0; i < n; i++)
{
if(strcmp(str[i],c) == 0)
return i;
}
return -1;
}
void input()
{
int i,j;
int m;
int w;
char s1[N],s2[N];
for(i = 0; i <= N-2; i++)
for(j = 0; j <= N-2; j++)
mp[i][j] = INF;
scanf("%d",&m);
n = 0;
strcpy(str[n++],"Park");
S = 0;
for(i = 0; i < m; i++)
{
scanf("%s %s %d",s1,s2,&w);
int x = find(s1);
if(x == -1)
{
x = n;
strcpy(str[n++],s1);
}
int y = find(s2);
if(y == -1)
{
y = n;
strcpy(str[n++],s2);
}
if(w < mp[x][y]) //可能有重边
mp[x][y] = mp[y][x] = w;
}
scanf("%d",&k);
}
void solve()
{
int i,j;
memset(vis,0,sizeof(vis));
memset(edge,0,sizeof(edge));
memset(father,-1,sizeof(father));
vis[S] = true;
int m = 0;
mst = 0;
//先求m度限制最小生成树
for(i = 0; i < n; i++)
{
if(!vis[i])
{
m++;
mst += prim(i);
}
}
int change; // 回路上权值最大的边,用于交换
int ax,bx,tmp;
for(i = m+1; i <= k && i < n; i++)
{
memset(best,-1,sizeof(best));
for(j = 0; j < n; j++)
{
if(best[j] == -1 && father[j] != S)
Best(j);
}
int minadd = INF; // 交换边的最小差值
for(j = 0; j < n; j++)
{
if(mp[S][j]!= INF && father[j] != S)
{
ax = best[j];
bx = father[ax];
tmp = mp[S][j] - mp[ax][bx];
if(tmp < minadd)
{
minadd = tmp;
change = j;
}
}
}
if (minadd >= 0) break; //用于度数不大于k的限制,如果k限制,就不用break了
mst += minadd;
ax = best[change];
bx = father[ax];
mp[ax][bx] = mp[bx][ax] = INF;
father[ax] = bx = S; // 改变生成树,将点ax直接指向源点S
mp[ax][S] = mp[S][ax] = mp[change][S];
mp[S][change] = mp[change][S] = INF;
}
}
int main()
{
input();
solve();
printf("Total miles driven: %d\n", mst);
return 0;
}
分享到:
相关推荐
题目来自POJ平台,编号为1639,题目名称为“Picnic Planning”,主要考查的是算法中的最小度限制生成树问题。 ### 问题定义 给定一个无向图,其中包含一个特殊节点s(称为公园),要求构建一个生成树,使得该生成树...
本讲座主要探讨最小生成树问题的两个拓展问题:最小度限制生成树和次小生成树。 1. 最小度限制生成树问题: 这个问题是在最小生成树的基础上添加了一个限制条件,即指定一个特殊顶点v0,要求这个顶点的度数(连接它...
而对于最小度限制生成树问题,可以在网络中确保某些关键节点有足够的连接度,从而提高整个网络的稳定性和可靠性。 总之,通过对最小生成树及其扩展问题的研究,我们可以更好地理解和解决现实生活中的各种复杂问题,...
最小度约束下的最小生成树算法是一种特殊的最小生成树问题,主要针对那些需要限制图中每个顶点度数的场景。最小生成树问题(Minimum Spanning Tree, MST)是图论中的经典问题,常用于网络优化,例如在运输、通信网络...
度约束最小生成树问题就是寻找一个图的生成树,使得每个节点的度数不超过预先设定的限制,同时整个生成树的权重最小。 在论文的第二章,作者专注于单点度约束最小生成树问题的研究。首先,他们提出了一个针对单点...
- **最小生成树(Minimum Spanning Tree)**:对于一个带权重的连通图,其最小生成树是所有生成树中边的权重总和最小的树。 #### 3. 图的连通性 - **连通图(Connected Graph)**:图中任意两个顶点间都存在路径的图。...
对于度受限的FSO网络,自下而上的最小度生成树算法在初始化阶段表现出优越性,而提出的路由算法则能进一步优化数据传输。这些研究成果为FSO网络的实际应用提供了理论基础和技术支持。 关键词:自由空间光通信,初始...
针对度受限的FSO网络,即每个节点的最大连接数量有限制的情况,论文提出了自下而上的最小度生成树算法作为初始化的首选方法。该算法从底层节点开始,逐步连接度数较小的节点,直到整个网络连通,以此达到最优的拓扑...
经过论证,对于度受限的FSO网络,自下而上的最小度生成树算法被证明是初始化阶段的首选方法,因为它能够在保证网络连通性的同时,最大限度地减少资源消耗。 关键词:自由空间光通信,初始化算法,生成树,路由算法 ...
这可能需要用到贪心算法(Greedy Algorithm)或者深度优先生成树(Depth First Search Tree)来枚举所有可能的操作序列,找到满足条件的最少操作数。 以上五个题目涵盖了图论、动态规划、回溯法、数论和贪心算法等...