题目链接:http://poj.org/problem?id=1751
题目大意:给出一些点,代表一个地方的坐标,还有给出一些已经修好的路,问还要修那几条,使得修的路的和长度最小。
分析:这道题,很明显最小生成树啊,可是有的地方已经修了,那也是最小生成树,那就在初始化完成后,让这两个地方距离为0就可以了,然后还需要记录路径。我开始是用kruskal做的,原因嘛,因为这个算法是加边的嘛,而prim是加点,但是呢,很明显这是一个稠密图,经验上来说,用prime更加贴切以一些。于是我就试了两种方法:
1,kruskal
这个添加的边是显然的,但要注意以下几点:
1,不必真正算出距离用sqrt,只需要相对距离es.cost=(x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);
2,注意并查集的写法,我以前写的并查集没有路径压缩,多亏了ZJ学姐指点:
int find(int x){
return par[x]==x?par[x]:par[x]=find(par[x]);
}
return par[x]==x?par[x]:par[x]=find(par[x]);
}
3,打印路径时可以在添加边时进行,不必开数组记录使用的边,等添加完后再打印。
4,另外,这个c++会TLE,g++400ms+,T到怀疑人生,如果有人知道为啥万望请一定告知!
code:
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAX_V=800;
const int INF=99999999;
int V,par[MAX_V],x[MAX_V],y[MAX_V],visit[MAX_V],rank[MAX_V];
struct edge{int u,v,cost;}es[MAX_V*MAX_V];
/*double dis(double x1,double y1,double x2,double y2){
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}*/
bool cmp(edge x,edge y){
return x.cost<y.cost;
}
int find(int x){
return par[x]==x?par[x]:par[x]=find(par[x]);
}
int main(void){
scanf("%d",&V);
for(int i=1;i<=V;++i)
scanf("%d%d",x+i,y+i);
int E=0;
for(int i=1;i<=V;++i)
for(int j=1;j<i;++j){
es[E].u=i;es[E].v=j;
es[E++].cost=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
}
int N;scanf("%d",&N);
for(int i=0;i<N;++i){
int a,b;scanf("%d%d",&a,&b);
es[E].u=a;es[E].v=b;
es[E++].cost=0;
}
for(int i=0;i<=V;++i)
par[i]=i,rank[i]=0;
sort(es,es+E,cmp);
for(int i=0;i<E;++i){
edge e=es[i];
int x=find(e.u),y=find(e.v);
if(x!=y){
if(rank[x]<rank[y])par[x]=y;
else {
par[y]=x;
if(rank[x]==rank[y])++rank[x];
}
if(e.cost)printf("%d %d\n",e.u,e.v);
}
}
//print
//printf("res:\n");
/* for(int i=0;i<res;++i)
if(es[visit[i]].cost)printf("%d %d\n",es[visit[i]].u,es[visit[i]].v);*/
}
#include<algorithm>
using namespace std;
const int MAX_V=800;
const int INF=99999999;
int V,par[MAX_V],x[MAX_V],y[MAX_V],visit[MAX_V],rank[MAX_V];
struct edge{int u,v,cost;}es[MAX_V*MAX_V];
/*double dis(double x1,double y1,double x2,double y2){
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}*/
bool cmp(edge x,edge y){
return x.cost<y.cost;
}
int find(int x){
return par[x]==x?par[x]:par[x]=find(par[x]);
}
int main(void){
scanf("%d",&V);
for(int i=1;i<=V;++i)
scanf("%d%d",x+i,y+i);
int E=0;
for(int i=1;i<=V;++i)
for(int j=1;j<i;++j){
es[E].u=i;es[E].v=j;
es[E++].cost=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
}
int N;scanf("%d",&N);
for(int i=0;i<N;++i){
int a,b;scanf("%d%d",&a,&b);
es[E].u=a;es[E].v=b;
es[E++].cost=0;
}
for(int i=0;i<=V;++i)
par[i]=i,rank[i]=0;
sort(es,es+E,cmp);
for(int i=0;i<E;++i){
edge e=es[i];
int x=find(e.u),y=find(e.v);
if(x!=y){
if(rank[x]<rank[y])par[x]=y;
else {
par[y]=x;
if(rank[x]==rank[y])++rank[x];
}
if(e.cost)printf("%d %d\n",e.u,e.v);
}
}
//printf("res:\n");
/* for(int i=0;i<res;++i)
if(es[visit[i]].cost)printf("%d %d\n",es[visit[i]].u,es[visit[i]].v);*/
}
prim
这个算法的思想是添加点,因此需要一个pre数组来记录,上一个点,稠密图用prim明显比较快。另外考虑到路径是双向的,故a[i][j]=a[j][i]=distance,另外如果有重边还要加判断,而kruskal就没这样的顾虑,因为它是按边的长短确定添加顺序的。
code:
#include<cstdio>
#define min(a,b) (a<b?a:b)
using namespace std;
const int MAXV=800,INF=99999999;
int cost[MAXV][MAXV],mincost[MAXV],prev[MAXV],V;
int x[MAXV],y[MAXV];
bool used[MAXV];
void prim(){
for(int i=1;i<=V;++i){
used[i]=false;
mincost[i]=INF;
prev[i]=1;
}
mincost[1]=0;
int res=0;
while(true){
int v=-1;
for(int u=1;u<=V;++u)
if(!used[u]&&(v==-1||mincost[u]<mincost[v]))v=u;
if(v==-1)break;
used[v]=true;//这一个点,prev数组记录另一个点
if(cost[v][prev[v]])printf("%d %d\n",v,prev[v]);
for(int u=1;u<=V;++u)
if(mincost[u]>cost[v][u]){
mincost[u]=cost[v][u];
prev[u]=v;//u是通过v更新的,因此prev[u]=v
}
}
}
int main(void){
scanf("%d",&V);
for(int i=1;i<=V;++i)
scanf("%d%d",x+i,y+i);
for(int i=1;i<=V;++i)
for(int j=1;j<=i;++j){
cost[i][j]=cost[j][i]=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
}
int N;scanf("%d",&N);
for(int i=0;i<N;++i){
int a,b;scanf("%d%d",&a,&b);
cost[a][b]=cost[b][a]=0;
}
prim();
}
#define min(a,b) (a<b?a:b)
using namespace std;
const int MAXV=800,INF=99999999;
int cost[MAXV][MAXV],mincost[MAXV],prev[MAXV],V;
int x[MAXV],y[MAXV];
bool used[MAXV];
void prim(){
for(int i=1;i<=V;++i){
used[i]=false;
mincost[i]=INF;
prev[i]=1;
}
mincost[1]=0;
int res=0;
while(true){
int v=-1;
for(int u=1;u<=V;++u)
if(!used[u]&&(v==-1||mincost[u]<mincost[v]))v=u;
if(v==-1)break;
used[v]=true;//这一个点,prev数组记录另一个点
if(cost[v][prev[v]])printf("%d %d\n",v,prev[v]);
for(int u=1;u<=V;++u)
if(mincost[u]>cost[v][u]){
mincost[u]=cost[v][u];
prev[u]=v;//u是通过v更新的,因此prev[u]=v
}
}
}
int main(void){
scanf("%d",&V);
for(int i=1;i<=V;++i)
scanf("%d%d",x+i,y+i);
for(int i=1;i<=V;++i)
for(int j=1;j<=i;++j){
cost[i][j]=cost[j][i]=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
}
int N;scanf("%d",&N);
for(int i=0;i<N;++i){
int a,b;scanf("%d%d",&a,&b);
cost[a][b]=cost[b][a]=0;
}
prim();
}