prim及其练习
关于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; }
好了,就到这吧!