您的位置:首页 > 其它

POJ - 1639 最小限制生成树

2017-08-18 15:57 363 查看
各位看到不足之处一定要留言指正啊!!!!!!!!!!!!!!!!!!!

Picnic Planning POJ - 1639

第一开始没发现,这个题的数据太水了,后来不删边也能过,于是第一次提交就A了, 后来写其他题发现算法不对, 才发现被这一题坑了

最小

题目大意

有若干个点, m条路, 给出路的起点,终点,权值,并且有一个点的度数有限制小于等于K, 求这棵树最小的权值和是多少

题目解读

抽象出来就是由限制的最小生成树

限制就是某个结点的度数小于等于K

如何求解此类问题最小K度限制生成树;

具体实现步骤如下

算法顺序

1 首先利用kruskal 算法求出各连通分量的最小生成树权值的和,并记录有边(这个有边指的是该边被选出来)相连的点

2 在计算各连通分量到指定点的最小距离, 并记录谁与指定点相连

结果 构成了最小m度生成树,并求出了值,并且记录了与谁相连接

3 搜索建立初始化动态规划初始值

4 遍历求得最小权值

5 求出最小M+1 度生成树

6 直到k

7 算出从m到k这些生成树中的最小值

代码

#define me(ar) memset(ar,0,sizeof(ar))
const int    INF = 1e8;
//......................................................
const int LEN = 30;
int K;
int cnt;
struct Road
{
int x,y;
int weight;
bool operator <(const Road &a) const
{
return weight < a.weight;
}
} road[LEN*LEN+10];//邻接表存边,Kruskal 算法要用
int Matrix[LEN][LEN];//邻接矩阵
int sign[LEN][LEN];//记录那些边是相连的
int vis[LEN];//记录是否相连
int F[LEN];//并查集所用
int Father[LEN];//由i到i+1度限制生成树需要用动态规划求解,用来状态转移
int Best[LEN];//Best[i] 指的是由当前节点到park这些边中最长边是多少
int Find(int x)//并查集所用Find函数
{
return x == F[x]?x:F[x] = Find(F[x]);
}
void  Dfs(int x)//Dfs动态规划记忆化搜索
{
//    vis[x] = 1;
for(int i = 1;i <= cnt; ++i )
{
if(sign[i][x]&!vis[i])//如果有边相连并且下一个节点没有被访问
{
if(x==0)
{
Best[i] = -INF;//与park 直接相连的边不能删除
}
else
{
Best[i] = max(Best[x],Matrix[x][i]);//状态转移方程
}
Father[i] = x;
vis[i] = 1;
Dfs(i);
}
}
}

int main(void)
{
int N;
while(cin>>N)
{
//初始化并查集数组
for(int i = 0;i < LEN; ++i)
F[i] = i;
me(sign);//初始化标记数组
me(vis);
//初始化邻接矩阵
for(int i = 0;i < LEN; ++i)
for(int j = 0;j < LEN; ++j)
Matrix[i][j] = INF;
cnt = 0;//用来记录共有多少个节点
set<string> se;
map<string,int> ma;//将地点编号
ma["Park"] = 0;//将park 加入节点
se.insert("Park");
string s1,s2;
int a,b;
int weight = 0;
for(int i = 0; i < N; ++i)
{
cin>>s1>>s2>>weight;
if(se.find(s1)!=se.end())
a = ma[s1];//如果节点已编号,则直接使用
else
a = ma[s1] = ++cnt,se.insert(s1);//如果没有编号,编号
if(se.find(s2)!=se.end())
b = ma[s2];
else
b = ma[s2] = ++cnt,se.insert(s2);
Matrix[a][b] = Matrix[b][a] = weight;
road[i].x = a;
road[i].y = b;
road[i].weight = weight;
}
//cout<<se.size()<<endl;
cin>>K;//最小k度生成树
int ans = 0;//kruskal 算法求最小生成树
sort(road,road+N);
for(int i = 0;i < N; ++i)
{
int x = road[i].x;
int y = road[i].y;
weight = road[i].weight;
if(x==0||y==0)//去除掉park这个点
continue;
int xx = Find(x);
int yy = Find(y);
if(xx!=yy)
{
F[xx] = F[yy];
ans += weight;
sign[x][y] = sign[y][x] = 1;
}
}
int Min[LEN];//用来记录每一个最小生成树到park点的最小路径
for(int i = 0;i < LEN; ++i)
Min[i] = INF;//初始化
int index[LEN];//用来记录最小路径的点
for(int i = 1;i <= cnt; ++i)
{
if(Matrix[i][0]<Min[Find(i)])
{
Min[Find(i)] = Matrix[i][0];
index[Find(i)] = i;
}
}
////        cout<<se.size()<<endl;
int m = 0;//用来记录除去park点即0点之后共有多少个连通分量
for(int i = 1;i <= cnt; ++i)
{
if(Min[i] != INF)
{
ans += Min[i];
sign[index[i]][0] = sign[0][index[i]] = 1;//将这个最小路径的点与park相连
m++;
}
}
int MMin = ans;
for(int i = m + 1; i <= K; ++i)//从m+1到K求最小i度生成树
{
me(vis);
vis[0] = 1;
Dfs(0);
int select = -1;//select用来记录选择哪个与park点相连是最小的
int sum = INF;
for(int i = 1;i <= cnt; ++i)
{
if(!sign[0][i] && Matrix[0][i] != INF)
{
if(Matrix[i][0]-Best[i]<sum)
{
select  = i;
sum = Matrix[i][0]-Best[i];
}
}
}
if(select == -1)//如果找不到,就跳出循环
break;
ans += sum;
sign[select][0] = sign[0][select] = 1;
MMin = min(MMin,ans);
for(int i = select; i != 0; i = Father[i])
{
if(Matrix[Father[i]][i]==Best[select])
{
sign[i][Father[i]] = sign[Father[i]][i] = 0;
break;
}
}

}
printf("Total miles driven: %d\n",MMin);
// cout<<MMin<<endl;
}
return 0;
}


类似题目

Arctic Network POJ - 2349

const int LEN = 500+100;
int S,P;
double Matrix[LEN][LEN];
int sign[LEN][LEN];
int vis[LEN];
int x[LEN];
int y[LEN];
double  dis[LEN];
int Father[LEN];
double  Best[LEN];
void Dfs(int x)
{
//    cout<<1<<endl;
for(int i = 1; i <= P; ++i)
{
if(!vis[i]&&sign[i][x])
{

if(x==0)
Best[i] = -INF;
else
Best[i] = max(Best[x],Matrix[x][i]);
vis[i] = 1;
Dfs(i);
Father[i] = x;
}
}
}
int main(void)
{
int N;
cin>>N;
while(N--)
{
cin>>S>>P;
me(sign);
me(vis);
for(int i = 1; i <= P; ++i)
{
scanf("%d %d",&x[i],&y[i]);
}
Matrix[0][1] = Matrix[1][0] = 0;
sign[0][1] = sign[1][0]= 1;
for(int i = 2; i <= P; ++i)
Matrix[0][i] = Matrix[i][0] = INF;
for(int i = 1; i <= P; ++i)
{
for(int j = i+1; j <= P; ++j)
{
Matrix[i][j] = Matrix[j][i] = sqrt((double)((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])));
}
}
//prim算法求解
vis[1] = 1;
int Left = P - 1;
for(int i = 1; i <= P; ++i)
{
dis[i] = Matrix[1][i];
Father[i] = 1;
}
double Min = INF;
int index = 0;
while(Left--)//prim算法
{
Min = INF;
for(int i = 1; i <= P; ++i)
{
if(!vis[i]&&dis[i]<Min)
{
Min = dis[i];
index = i;
}
}
vis[index] = 1;
if(Min == INF)
break;
sign[index][Father[index]] = sign[Father[index]][index] = 1;
for(int i = 1; i <= P; ++i)
{
if(!vis[i]&&Matrix[index][i]<dis[i])
{
dis[i] = Matrix[index][i];
Father[i] = index;
}
}
}
double Max;
for(int k = 2; k <= S; ++k)
{

me(vis);
vis[0] = 1;
Father[0] = -1;
Dfs(0);
Max = -INF;
index = 0;
for(int i = 1; i <= P; ++i)
{
if(Max<Best[i])
{
Max =Best[i];
index = i;
}
}
sign[index][0] = sign[0][index] = 1;//将边加进去
for(int i = index; Father[i] != -1; i = Father[i] )//删边
{
if(Max == Matrix[Father[i]][i])
{
sign[i][Father[i]] = sign[Father[i]][i] = 0;
break;
}
}
}
me(vis);
vis[0] = 1;

//        Father[0] = -1;
Dfs(0);
Max = -INF;
index = 2;
for(int i = 1; i <= P; ++i)
{
if(Max<Best[i])
{
Max =Best[i];
index = i;
}
}
if(Max == -INF)
cout<<0<<endl;
else
printf("%.2f\n",Max);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息