【最小生成树】图论复习(三)

来源:互联网 发布:股票交易记录数据 编辑:程序博客网 时间:2024/09/21 08:15

最小生成树就两种算法。一种Prim,另一种Kruscal,因为Prim酷似Dijkstra,我又比较喜欢写Dijkstra,所以我还是经常写Prim,当然偶尔也写写Kruscal,感觉效率上要高一点。这里就主要发Kruscal吧。

毕竟处于入门阶段,做的题也不会太难,大牛直接忽略吧。

第一题,按照惯例是裸题。造路行动

Kruscal写的,没什么好说。

代码:

#include<cstdio>#include<cstdlib>#include<cstring>using namespace std;const int maxn=100+10;struct edge{       int left;       int right;       int len;       bool flag;}edges[maxn];int f[maxn];int n,k;int cmp(const void *a,const void *b){    return (*(edge *)a).len - (*(edge *)b).len;}void init(){     freopen("rqnoj370.in","r",stdin);     freopen("rqnoj370.out","w",stdout);}int find(int x){    if(f[x]==x)return x;    f[x]=find(f[x]);    return f[x];}void merge(int u,int v){     int fu=find(u);     int fv=find(v);     if(fu!=fv)f[fu]=fv;}void predoing(){     for(int i=1;i<=n;i++)       f[i]=i;}void readdata(){     scanf("%d%d",&n,&k);     predoing();     for(int i=1;i<=k;i++)     {         scanf("%d%d%d",&edges[i].left,&edges[i].right,&edges[i].len);     }}bool query(int a,int b){    if(find(a)!=find(b))return true;      return false;        }void solve(){     int ans=0;     int haveput=1;     int i=1;     qsort(&edges[1],k,sizeof(edges[0]),cmp);     while(haveput < n)     {         if(query(edges[i].left,edges[i].right))         {             edges[i].flag=true;             merge(edges[i].left,edges[i].right);             haveput++;         }         i++;     }     for(int i=1;i<=k;i++)     {         if(edges[i].flag==false)         {            ans+=edges[i].len;         }     }     printf("%d",ans);     }int main(){    init();    readdata();    solve();    return 0;}

第三题:联络员

在裸题的基础上变了形,不过也比较简单。如果是必选渠道就在读入的时候先加入生成树,然后再做一次MST,累加出来的ans就是所求了。

代码:

#include<cstdio>#include<cstring>#include<cstdlib>using namespace std;const int maxn = 2000 + 10;const int maxm = 10000 + 10;struct edge {    int l,r,w; }edges[maxm];int f[maxn];int n,m;int ans = 0;int haveput = 0;void init(){    freopen("tyvj1307.in","r",stdin);    freopen("tyvj1307.out","w",stdout);}int cmp(const void *a,const void *b){    return (*(edge *)a).w -  (*(edge *)b).w;}int find(int x){    if(f[x] == x)return x;    f[x] = find(f[x]);    return f[x];}void predoing(){    for(int i = 1;i <= n;i++)    {        f[i] = i;    }}void merge(int x,int y){    int wx = find(x);    int wy = find(y);    f[wx] = wy;}void readdata(){    scanf("%d%d",&n,&m);    predoing();    for(int i = 1;i <= m;i++)    {        int x,y,w,l;        scanf("%d%d%d%d",&l,&edges[i].l,&edges[i].r,&edges[i].w);        if(l == 1)        {                ans += edges[i].w;            if(find(edges[i].l) != find(edges[i].r))            {                merge(edges[i].l,edges[i].r);                haveput++;            }        }            }}int solve(){    qsort(&edges[1],m,sizeof(edges[0]),cmp);        int i = 1;    while(haveput < n - 1)    {        if(find(edges[i].l) != find(edges[i].r))        {            ans += edges[i].w;            merge(edges[i].l,edges[i].r);            ++haveput;        }        i++;    }    printf("%d",ans);}int main(){    init();    readdata();    solve();    return 0;}
最后一道:mty的宝藏

也属于一道变型,判断一个图中有没有环。因为我们知道在最小生成树中必然是不存在环的,所以我们可以在读入时累加,在求出MST与累加的值比较,如果MST=累加值,那么不存在环,如果MST<累加值,说明图中存在了环,明白了这个代码就好写了,这道题是用Prim做的。

代码:

#include<cstdio>#include<cstring>#include<queue>using namespace std;const int maxn = 1000 + 10;int map[maxn][maxn];int dist[maxn];bool done[maxn];int n,com,sum;void init(){     freopen("rqnoj564.in","r",stdin);     freopen("rqnoj564.out","w",stdout);}void readdata(){    memset(done,false,sizeof(done));    memset(dist,67,sizeof(dist));    sum = com = 0;    scanf("%d",&n);    for(int i = 1;i <= n;i++)        for(int j = 1;j <= n;j++)        {            int p;            scanf("%d",&p);            if(p)            {                map[i][j] = 1;                sum += 1;            }        }     }void solve(){     com = 0;     typedef pair<int,int> pii;     priority_queue<pii,vector<pii>,greater<pii> >q;     dist[1] = 0;     q.push(make_pair(dist[1],1));     for(int i = 1;i <= n;i++)     {         pii u = q.top(); q.pop();         while(done[u.second])         {             u = q.top();             q.pop();         }         int k = u.second;         done[k] = true;         com += dist[k];         for(int j = 1;j <= n;j++)         {             if(!map[k][j])continue;             if(done[j])continue;             if(map[k][j] < dist[j])             {                 dist[j] = map[k][j];                 q.push(make_pair(dist[j],j));             }         }     }     if(com == sum)         printf("%c",'N');     else          printf("%c",'Y');}int main(){    init();    readdata();    solve();    return 0;}

总结:最小生成树以及最短路是图论中的经典问题,NOIP也很可能考到,一定要注意灵活运用,不能太死板,要多多训练自己抽象模型的能力。
(Ps:发这么些水题出来感觉有点。。。不过也算了,人都是会慢慢成长的,大牛在最开始不也是弱菜么?我现在要努力向前!)