关于prim,其实我今天才学...
prim其实就是最小生成树的一种算法,严格每次的找最小边连到树上。看书上的代码看不懂,于是就自己大胆用堆优化写prim。
搞了很长时间,经过不写努力,还是搞出来了。
代码如下:
inline void prim() {priority_queue<pair<int,int> >q;q.push(make_pair(0,m));while(ans<m){int y=q.top().second;int v=-q.top().first;q.pop();if(vis[y]!=1){minn+=v;ans++;vis[y]=1;for(int i=link[y];i;i=a[i].next){int y1=a[i].y;int v1=a[i].v;q.push(make_pair(-v1,y1));}}} }
有点简陋,但个人还是觉得比较理解,也就是每次加进去一个点,把这个点能连到的点与边权加进队列。之后取出最小的边,判断是否在树里,直到最后所有的点都在树里,prim结束。最小生成树也就完成了。
不说了,来看一道"裸题":
猛看这道题以为就是练格式的,之后就发现不对了。他要保证的是每个地点有水即不一定要连成一个树,还可以在连起来的代价大于打井的代价时选择打井。连起来的大家都会,不就是连个最小生成树嘛,但是加上打井就有疑问,怎样处理好打井与用管道连起来的关系就是这道题的难点所在。
大家可以这样想:打井之后就不在树里,那我们最小生成树就无法完成。这时怎样将打井和树联系起来就是要思考的问题了。既然打井之后不在树里,哪我们为什么不能把它加进树里,即一个打井费用就是一个边,一共有n打井费用,即n条边,那就可以再加一个点,使其他点到这个点的距离就是那个点的打井费用,这样一共(n+1)个点跑最短路,与n所连的点就是打井的点,最小树也就完成了。
以下是代码:
#include<bits/stdc++.h>
using namespace std;
int n,tot,minn,link[100000],m,ans,vis[100000];
struct bian
{int y,v,next;
};
bian a[1000000];
inline int read()
{int x=0,ff=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-') ff=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*ff;
}
inline void add(int x,int y,int v)
{a[++tot].y=y;a[tot].v=v;a[tot].next=link[x];link[x]=tot;
}
inline void prim()
{priority_queue<pair<int,int> >q;q.push(make_pair(0,m));while(ans<m){int y=q.top().second;int v=-q.top().first;q.pop();if(vis[y]!=1){minn+=v;ans++;vis[y]=1;for(int i=link[y];i;i=a[i].next){int y1=a[i].y;int v1=a[i].v;q.push(make_pair(-v1,y1));}}}
}
int main()
{//freopen("1.in","r",stdin);n=read();m=n+1;for(int i=1;i<=n;i++){int v=read();add(m,i,v);add(i,m,v);}for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){int v=read();if(i!=j) add(i,j,v);}}prim();cout<<minn<<endl;return 0;
}
好了,就到这吧!