各位看到不足之处一定要留言指正啊!!!
###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;
}