最小生成树大汇总

、最小生成树:v + v + value布线问题 

布线问题:由于安全问题,只能选择一个楼连接到外界供电设备。

/*

1

v e

4 6(4个顶点,正好六个边

 

1 2 10

2 3 10

3 1 10

1 4 1

2 4 1 

3 4 1

 

1 3 5 6(连接到外界供电设备所需要的费用)

*/

 

#include<iostream>

using  namespace std;

#define MAX 505

#define MAXCOST 0x7fffffff

 

int graph[MAX][MAX];

 

int prim(int graph[][MAX], int n)

{

int lowcost[MAX];

int mst[MAX];

int i, j, min, minid, sum = 0;

for (i = 2; i <= n; i++)

{

lowcost[i] = graph[1][i];

mst[i] = 1;

}

mst[1] = 0;

for (i = 2; i <= n; i++)

{

min = MAXCOST;

minid = 0;

for (j = 2; j <= n; j++)

{

if (lowcost[j] < min && lowcost[j] != 0)

{

min = lowcost[j];

minid = j;

}

}

//cout << "V" << mst[minid] << "-V" << minid << "=" << min << endl;

sum += min;

lowcost[minid] = 0;

for (j = 2; j <= n; j++)

{

if (graph[minid][j] < lowcost[j])

{

lowcost[j] = graph[minid][j];

mst[j] = minid;

}

}

}

return sum;

}

 

int main()

{

int i, j, k, m, n;

int x, y, cost;

int zushu;

cin>>zushu;

while(zushu--)

{

    cin >> m >> n;//m=顶点的个数,n=边的个数

for (i = 1; i <= m; i++)

{

for (j = 1; j <= m; j++)

{

graph[i][j] = MAXCOST;

}

}

for (k = 1; k <= n; k++)

{

cin >> i >> j >> cost;

graph[i][j] = cost;

graph[j][i] = cost;

}

    cost = prim(graph, m);

    

    int min_v=MAXCOST;

for(int i=1;i<=m;++i)

{

int t;

    cin>>t;

if(t<min_v)

min_v=t;

}

    cout <<cost+min_v<< endl;

}

return 0;

}

 

 

3、prim算法之矩阵类型

/*

1(组数)

 

33*3的矩阵)

0 990 692

990 0 179

692 179 0

 

//一个有n个顶点的无向图最多几条边 n(n-1)/2

//具有n个顶点的无向图,至少应有多少n-1条边才能确保是一个连通图

 

*/

 

#include "stdio.h"

#include "string.h"

#define N 500

#define INT 10000

bool vis[N];

int dis[N];

int a[N][N];

int main(){

int t;

scanf("%d", &t);

while(t--){

int n;

scanf("%d", &n);

int i,j,temp,k;

memset(vis,0,sizeof(vis));

for(i=1;i<=n;++i){

for(j=1;j<=n;++j){

scanf("%d", &a[i][j]);

}

}

for(i=1;i<=n;++i){

dis[i]=INT;

}

dis[1]=0;

for(i=1;i<=n;++i){

temp=INT;

k=0;

for(j=1;j<=n;++j){

if(!vis[j]&&dis[j]<temp){

temp=dis[j];

k=j;

}

}

vis[k]=1;

for(j=1;j<=n;++j){

if(!vis[j]&&dis[j]>a[k][j]){

dis[j]=a[k][j];

}

}

}

int max=0;

for(i=1;i<=n;++i){

if(max<dis[i])

max=dis[i];

}

printf("%d\n",max);

}

return 0;

}

 

4、输出最小生成树个边权值累加和(矩阵类型)

4

0 4 9 21

4 0 8 17

9 8 0 16

21 17 16 0

#include <stdio.h>

#include <string.h>

#define MaxInt 0x3f3f3f3f

#define N 110

int map[N][N],low[N],visited[N];

int n;

 

int prim()

{

    int i,j,pos,min,result=0;

    memset(visited,0,sizeof(visited));

    visited[1]=1;pos=1;

    for(i=1;i<=n;i++)

        if(i!=pos) low[i]=map[pos][i];

    for(i=1;i<n;i++)

    {

     min=MaxInt;

     for(j=1;j<=n;j++)

         if(visited[j]==0&&min>low[j])

         {

             min=low[j];pos=j;

         }

    result+=min;

    visited[pos]=1;

//更新权值

    for(j=1;j<=n;j++)

        if(visited[j]==0&&low[j]>map[pos][j])

            low[j]=map[pos][j];

    }

    return result;

}

 

int main()

{

    int i,v,j,ans;

    while(scanf("%d",&n)!=EOF)

    {

        memset(map,MaxInt,sizeof(map));

        for(i=1;i<=n;i++)

            for(j=1;j<=n;j++)

            {

                scanf("%d",&v);

                map[i][j]=map[i][j]=v;

            }

            ans=prim();

            printf("%d\n",ans);

    }

    return 0;

}

 

 

 

 

5、求出最小生成树中边的最大值(矩阵类型)

1

3

0 990 692

990 0 179

692 179 0

 

 

692

 

 

 

 

#include<stdio.h>

#define MAX 505

#define inf 999999

int c[MAX][MAX];

int n;

 

 

void prim()

{

    int lowcost[MAX ];

    int closest[MAX ];

    bool s[MAX ];

    s[1]=true;

    for(int i=2;i<=n;i++)

    {

        lowcost[i]=c[1][i];

        closest[i]=1;

        s[i]=false;

    }

    for(int i=1;i<=n;i++)

    {

        int min=inf;

        int j=i;

        for(int k=2;k<=n;k++)

        if((lowcost[k]<min)&&(!s[k]))

        {

            min=lowcost[k];

            j=k;

        }

 

 

 

 

 

 

       //cout<<j<<" "<<closet[j]<<endl;输出最小生成树的路径。

 

 

        s[j]=true;

        for(int k=2;k<=n;k++)

        {

            if((c[j][k]<lowcost[k])&&(!s[k]))

            {

                lowcost[k]=c[j][k];

                closest[k]=j;

            }

        }

    }

 

 

 

 

//最小生成树的边值已经放大lowcost数组中了。遍历一下就可以得到最大最小值。

    int result=-1;

    for(int i=2;i<=n;i++)

       {

           if(result<lowcost[i])

            result=lowcost[i];

       }

       printf("%d\n",result);

}

int main()

{

    int t;

    scanf("%d",&t);

    while(t--)

    {

        scanf("%d",&n);

        for(int i=1;i<=n;i++)

        {

            for(int  j=1;j<=n;j++)

            {

                scanf("%d",&c[i][j]);

            }

        }

        prim();

    }

    return 0;

}

 

 

 

 

6、prim算法变形之引水工程(矩阵型)完全矩阵

 /*1

5

  0 5 4 4 3 6

  5 0 2 2 2 2

  4 2 0 3 3 3

  4 2 3 0 4 5

  3 2 3 4 0 1

  5 2 3 5 1 0*/

#include <stdio.h>

#include <string.h>

#define MaxInt 0x3f3f3f3f

#define N 303

int map[N][N],low[N],visited[N];

int n;

 

int prim()

{

    int i,j,pos,min,result=0;

    memset(visited,0,sizeof(visited));

    visited[1]=1;pos=1;

    for(i=1;i<=n;i++)

        if(i!=pos) low[i]=map[pos][i];

    for(i=1;i<n;i++)

    {

     min=MaxInt;

     for(j=1;j<=n;j++)

         if(visited[j]==0&&min>low[j])

         {

             min=low[j];

 pos=j;

         }

    result+=min;

    visited[pos]=1;

    for(j=1;j<=n;j++)

        if(visited[j]==0&&low[j]>map[pos][j])

            low[j]=map[pos][j];

    }

    return result;

}

 

int main()

{

    int i,v,j,ans,zushu;

    scanf("%d",&zushu);

    int a[303];

    while(zushu--)

    {

        

        scanf("%d",&n);

        memset(map,MaxInt,sizeof(map));

        

        map[1][1]=0;

        for(i=2;i<=n+1;++i)

        {

          scanf("%d",&map[i][1]);

          map[1][i]=map[i][1];

        }

       

        getchar();

        for(i=2;i<=n+1;i++)

            for(j=2;j<=n+1;j++)

            {

                scanf("%d",&v);

                map[i][j]=map[i][j]=v;

            }

            

        

        n=n+1;

         ans=prim();

           printf("%d\n",ans);

    }

    return 0;

}

 

 

        

7、Kruskal(克鲁斯卡尔)(不是完全边,v+v+value

/*

但不一定有直接的公路相连,只要能间接通过公路可达即可

村庄从1M编号。当N0时,全部输入结束,相应的结果不要输出。

对每个测试用例,在1行里输出全省畅通需要的最低成本。

若统计数据不足以保证畅通,则输出“?”。

Sample Input

 

3 3

1 2 1

1 3 2

2 3 4

 

1(e) 3(v)

2 3 2

0 100

 

 

Sample Output

3

?

*/

 

 

 

#include<iostream>

#include<cstring>

#include<string>

#include<cstdio>

#include<algorithm>

using namespace std;

#define MAX 100

int father[MAX], son[MAX];

int v, l;

 

typedef struct Kruskal //存储边的信息

{

int a;

int b;

int value;

};

 

bool cmp(const Kruskal & a, const Kruskal & b)

{

return a.value < b.value;

}

 

int unionsearch(int x) //查找根结点+路径压缩

{

return x == father[x] ? x : unionsearch(father[x]);

}

 

bool join(int x, int y) //合并

{

int root1, root2;

root1 = unionsearch(x);

root2 = unionsearch(y);

if(root1 == root2) //为环

return false;

else if(son[root1] >= son[root2])

{

father[root2] = root1;

son[root1] += son[root2];

}

else

{

father[root1] = root2;

son[root2] += son[root1];

}

return true;

}

 

int main()

{

int ltotal, sum, flag;

Kruskal edge[MAX];

//scanf("%d", &ncase);

while(scanf("%d%d", &l, &v),l)

{

ltotal = 0, sum = 0, flag = 0;

for(int i = 1; i <= v; ++i) //初始化

{

father[i] = i;

son[i] = 1;

}

for(int i = 1; i <= l ; ++i)

{

scanf("%d%d%d", &edge[i].a, &edge[i].b, &edge[i].value);

}

sort(edge + 1, edge + 1 + l, cmp); //按权值由小到大排序

int count=0;

for(int i = 1; i <= l; ++i)

{

if(join(edge[i].a, edge[i].b))

{

ltotal++; //边数加1

sum += edge[i].value; //记录权值之和

//cout<<edge[i].a<<"->"<<edge[i].b<<endl;

}

if(ltotal == v - 1) //最小生成树条件:边数=顶点数-1

{

flag = 1;

break;

}

count++;

}

 //printf("count%d\n", count);

if(flag)

printf("%d\n",sum);

else

printf("?\n");

}

return 0;

}

8、还差多少对v+v才能达到最小生成树

/*

 

目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,

只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?

v e

 

4 2

1 3

4 3

 

3 3

1 2

1 3

2 3

 

5 2

1 2

3 5

 

999 0

0

输出最少还需要建设的道路数目。

*/

 

#include<iostream>

#include<cstdio>

#include<cstring>

#include<cmath>

#include<algorithm>

using namespace std;

int pre[1010];

 

int unionsearch(int root) //查找父节点+路径压缩(非递归)

{

    int son, tmp;

    son = root;

    while(root != pre[root])

        root = pre[root];

    while(son != root)

    {

        tmp = pre[son];

        pre[son] = root;

        son = tmp;

    }

    return root;

}

 

/*int unionsearch(int root) //查找父节点+路径压缩(递归)

{

return root == pre[root] ? root : unionsearch(pre[root]);

}*/

 

int main()

{

    int num, road, total, i, start, end, root1, root2;

    while(scanf("%d%d", &num, &road) && num)

    {

        total = num - 1;

        for(i = 1; i <= num; ++i)

            pre[i] = i;

        while(road--)

        {

            scanf("%d%d", &start, &end);

            root1 = unionsearch(start);

            root2 = unionsearch(end);

            if(root1 != root2)

            {

                pre[root1] = root2;

                total--;

            }

        }

        printf("%d\n", total);

    }

    return 0;

}

 

9、先判断是否畅通,再用最小生成树求最小花费

/*

对每个测试用例,1 行里输出全省畅通需要的最低成本。

若统计数据不足以保证畅通,则输出 No solution

2

e v

3 3

1 2 1

1 3 2

2 3 4

 

 

1 3

2 3 2

样例输出

3

No solution

*/

 

#include<iostream>

#include<cstdio>

#include<cstring>

#include<cmath>

#include<algorithm>

using namespace std;

//判断是否畅通

int pre[1010];

int unionsearch(int root) //查找父节点+路径压缩(非递归)

{

    int son, tmp;

    son = root;

    while(root != pre[root])

        root = pre[root];

    while(son != root)

    {

        tmp = pre[son];

        pre[son] = root;

        son = tmp;

    }

    return root;

}

//最小生成树

 

#define MAX 505

#define MAXCOST 0x7fffffff

int graph[MAX][MAX];

 

int prim(int graph[][MAX], int n)

{

int lowcost[MAX];

int mst[MAX];

int i, j, min, minid, sum = 0;

for (i = 2; i <= n; i++)

{

lowcost[i] = graph[1][i];

mst[i] = 1;

}

mst[1] = 0;

for (i = 2; i <= n; i++)

{

min = MAXCOST;

minid = 0;

for (j = 2; j <= n; j++)

{

if (lowcost[j] < min && lowcost[j] != 0)

{

min = lowcost[j];

minid = j;

}

}

//cout << "V" << mst[minid] << "-V" << minid << "=" << min << endl;

sum += min;

lowcost[minid] = 0;

for (j = 2; j <= n; j++)

{

if (graph[minid][j] < lowcost[j])

{

lowcost[j] = graph[minid][j];

mst[j] = minid;

}

}

}

return sum;

}

 

int main()

{

    int num, road, total, start, end, root1, root2;

    

    int i, j, k, m, n;

int x, y, cost;

int zushu;

cin>>zushu;

while(zushu--)

{

    cin >>  n >> m ;//m=顶点的个数,n=边的个数

    num=m,road=n;

for (i = 1; i <= m; i++)

{

for (j = 1; j <= m; j++)

{

graph[i][j] = MAXCOST;

}

}

//------------判断是否畅通

     //num顶点,road道路

        total = num - 1;

        for(i = 1; i <= num; ++i)

            pre[i] = i;

            

            

for (k = 1; k <= n; k++)

{

cin >> i >> j >> cost;

graph[i][j] = cost;

graph[j][i] = cost;

start=i;

end=j;

root1 = unionsearch(start);

            root2 = unionsearch(end);

            if(root1 != root2)

            {

                pre[root1] = root2;

                total--;

            }

}

       

        //cout<<total<<endl;

        if(total<1)

        {

          cost = prim(graph, m);

          printf("%d\n",cost);

        }

        else{

         printf("No solution\n");

        }

}

    return 0;

}

 

10、dfs 欧拉回路

/*

2

v e

4 3

 

1 2

1 3

1 4

 

一笔画就yes,不一笔画no

*/

#include <iostream>

#include <cstdio>

#include <cstring>

#include <cstdlib>

#include <vector>

#include <climits>

#include <algorithm>

#include <cmath>

#define LL long long

using namespace std;

vector<int>e[1010];

int deg[1010];

bool vis[1010];

void dfs(int u) {

    vis[u]  = true;

    for(int i = 0; i < e[u].size(); i++) {

        if(!vis[e[u][i]]) {

            dfs(e[u][i]);

        }

    }

}

int main() {

    int kase,p,q,i,u,v,temp;

    bool flag;

    scanf("%d",&kase);

    while(kase--) {

        scanf("%d %d",&p,&q);

        memset(deg,0,sizeof(deg));

        memset(vis,false,sizeof(vis));

        for(i = 0; i < 1010; i++)

            e[i].clear();

        for(i = 0; i < q; i++) {

            scanf("%d %d",&u,&v);

            e[u].push_back(v);

            e[v].push_back(u);

            deg[u]++;

            deg[v]++;

        }

        dfs(1);

        flag = true;

        for(i = 1; i <= p; i++)

            if(!vis[i]) {

                flag = false;

                break;

            }

        if(flag) {

            for(temp = 0,i = 1; i <= p; i++) {

                if(deg[i]&1) {

                    temp++;

                    if(temp > 2) break;

                }

            }

        }

        if(!flag || temp > 2) puts("No");

        else puts("Yes");

    }

    return 0;

}

 

 

12、次小生成树(v+v+value型最小花费方案个数)

 

 

/*N个不同的城市里,这些城市分别编号1~N

如果存在两种以上的最小花费方案则输出Yes,如果最小花费的方案只有一种,则输出No)

样例输入

2(组数)

 

3v3e

v+v+value

1 2 1

2 3 2

3 1 3

 

4 4

1 2 2

2 3 2

3 4 2

4 1 2

样例输出

No

Yes

*/

 

#include<iostream>

#include<stdio.h>

#include<string>

#include<cstring>

#include<algorithm>

using namespace std;

#define MaxV 510

#define MaxE 200005

struct Edge

{

 int x,y,dis;

};

Edge edge[MaxE],edge1[MaxE];        //分别是边数组 最小生成树中的边数组

int father[MaxV],Num[MaxV],num,dex,dey;         //并查集 num统计生成树边的条数 dex dey指枚举删除边的x,y坐标

void Init(int V)         //并查集初始化,单个元素自成集合

{

 for(int i=1;i<=V;i++)

 {

  father[i]=i;

  Num[i]=1;

 }

}

int findfather(int x)      //寻找父结点,可以压缩路径。。

{

 for(x;x!=father[x];x=father[x]) ;

 return father[x];

}

void Union(int x,int y)

{

 int t1,t2;

 t1=findfather(x);

 t2=findfather(y);

 if(Num[t1]>Num[t2])

 {

  father[t2]=t1;

  Num[t1]+=Num[t2];

 }

 else

 {

  father[t1]=t2;

  Num[t2]+=Num[t1];

 }

}

int comp(const void* p1,const void* p2)

{

 return (*(Edge*)p1).dis>(*(Edge*)p2).dis;

}

int Krusual(int V,int E)

{

 int sum=0;

 for(int i=1;i<=E;i++)

 {

  if(findfather(edge[i].x)!=findfather(edge[i].y))

  {

   sum+=edge[i].dis;

   Union(edge[i].x,edge[i].y);

   edge1[++num]=edge[i];

  }

 }

 return sum;

}

int AKrusual(int V,int E)

{

 Init(V);

 int sum=0;

 qsort(edge+1,E,sizeof(edge[1]),comp);

 int k;

 for(k=1;k<=E;++k)

 {

  if(findfather(edge[k].x)!=findfather(edge[k].y))

  {

   if(edge[k].x==dex&&edge[k].y==dey)

   {continue;}

   if(edge[k].x==dey&&edge[k].x==dex)

   {continue;}

   sum+=edge[k].dis;

   Union(edge[k].x,edge[k].y);

  }

 }

 return sum;

}

bool Judge(int V)      //判断图是否连通,不连通则无法构造最小生成树

{

 for(int m=1;m<=V-1;++m)

 if(findfather(m)!=findfather(m+1))

 return false;

 return true;

}

int main()

{

 int test,j,V,E;

 scanf("%d",&test);

 while(test--)

 {

  scanf("%d%d",&V,&E);

  num=0;

  for(j=1;j<=E;++j)

  {

   scanf("%d%d%d",&edge[j].x,&edge[j].y,&edge[j].dis);

  }

  qsort(edge+1,E,sizeof(edge[1]),comp);

  Init(V);

  int sum=Krusual(V,E);

  int M=1000,temp;

  for(int q=1;q<=num;++q)

  {

   dex=edge1[q].x;

   dey=edge1[q].y;

   temp=AKrusual(V,E);

   if(temp<M&&Judge(V))

   M=temp;

   if(M==sum) break;

  }

  if(M==sum) printf("Yes\n");

  else printf("No\n");

 }

}  

 

 

 

 

/*

13判断最小生成树是否唯一

    构成原最小生成树的边,一条边一条边的删。

*/

 

 

#include"stdio.h"

#include"stdlib.h"

 

 

struct A

{

int a,b;

int flag;

int len;

}eage[5555];

int n,m;

int set[111];

 

 

int cmp(const void *a,const void *b)

{

struct A *c,*d;

c=(struct A *)a;

d=(struct A *)b;

return c->len-d->len;

}

 

 

void build(int num)

{

int i;

for(i=1;i<=num;i++) set[i]=i;

}

int find(int k)

{

if(set[k]==k) return k;

set[k]=find(set[k]);

return set[k];

}

void Union(int f1,int f2)

{

set[f1]=f2;

}

 

 

int Kruskal(int t_d)

{

int i;

int ans;

int f1,f2;

int count=1;

 

 

ans=0;

for(i=0;i<m;i++)

{

if(count==n) break;

if(i==t_d) continue;

f1=find(eage[i].a);

f2=find(eage[i].b);

if(f1==f2) continue;

Union(f1,f2);

eage[i].flag=1;

ans+=eage[i].len;

count++;

}

 

 

return ans;

}

 

 

int main()

{

int T;

int z,i;

int a,b,c;

int ans,t_ans;

int del[111],k;

int flag;

int temp;

 

 

scanf("%d",&T);

while(T--)

{

scanf("%d%d",&n,&m);

//m e n v

 

build(n);

 

 

for(i=0;i<m;i++)

{

scanf("%d%d%d",&a,&b,&c);

eage[i].a=a;

eage[i].b=b;

eage[i].len=c;

eage[i].flag=0;

}

 

 

qsort(eage,m,sizeof(eage[0]),cmp);

ans=Kruskal(-1);

temp=0;

for(i=1;i<=n;i++) if(set[i]==i) temp++;

if(temp>1) {printf("0\n");continue;}

k=0;

for(i=0;i<m;i++) if(eage[i].flag) del[k++]=i;

 

 

flag=0;

for(z=0;z<k;z++)

{

build(n);

t_ans=Kruskal(del[z]);

temp=0;

for(i=1;i<=n;i++) if(set[i]==i) temp++;

if(temp>1) continue;

if(t_ans==ans) {flag=1;break;}

}

 

 

if(flag) printf("Not Unique!\n");

else printf("%d\n",ans);

}

return 0;

}

 

 

经典问题:对于每一条边存在两个权值,分别是花费和长度,要生成一个树,使得总的花费比上总的长度最小。   

       

13、最优比率生成树 (最优比例生成树)

 

 

 

/*

 

4(v)

x y z

0 0 0

0 1 1

1 1 2

1 0 3

0

 

cost[i][j]=cost[j][i]=abs(z[i]-z[j]);//花费为海拔之差

dist[i][j]=dist[j][i]=sqrt(t);//距离为 点与点的水平距离

output:  the minimum ratio of overall cost of the channels to the total length

*/

 

 

#include<iostream>

#include<cstdio>

#include<cstring>

#include<algorithm>

#include<cmath>

#include<queue>

using namespace std;

#define N 1010

#define MAX 999999999

const double eps=1e-4;

int n;

int vis[N],x[N],y[N],z[N],pre[N];

double dis[N],cost[N][N],dist[N][N];

double prim(double x){

    double totalcost=0,totaldist=0;

    for(int i=1;i<=n;i++){

        pre[i]=1;

    }

    dis[1]=0;

    memset(vis,0,sizeof(vis));

    vis[1]=1;

    for(int i=2;i<=n;i++){

        dis[i]=cost[1][i]-dist[1][i]*x;

    }

    int k;

    for(int i=2;i<=n;i++){

        double mincost=MAX;

        for(int j=2;j<=n;j++){

            if(!vis[j]&&dis[j]<mincost){

                mincost=dis[j];

                k=j;

            }

        }

        vis[k]=1;

        totalcost+=cost[pre[k]][k];

        totaldist+=dist[pre[k]][k];

        for(int j=1;j<=n;j++){

            if(!vis[j]&&dis[j]>cost[k][j]-dist[k][j]*x){

                dis[j]=cost[k][j]-dist[k][j]*x;

                pre[j]=k;

            }

        }

    }

    return totalcost/totaldist;

}

int main(){

    while(scanf("%d",&n),n){

        for(int i=1;i<=n;i++){

            scanf("%d%d%d",&x[i],&y[i],&z[i]);

            for(int j=1;j<i;j++){

                double t=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);

                

                

                cost[i][j]=cost[j][i]=abs(z[i]-z[j]);//花费为海拔之差

                dist[i][j]=dist[j][i]=sqrt(t);//距离为 点与点的水平距离

            }

        }

        double a=0;

        while(1){

            double b=prim(a);

            if(abs(a-b)<eps)break;

            else a=b;

            //cout<<a<<endl;

        }

        printf("%.3f\n",a);

    }

    return 0;

}

 

 

14、判断边与一个图最小生成shu的关系

 

题意:给出一张带权无向图,然后询问该图的每条边进行询问,若这条边出现在该图的所有最小生成树中,输出any;若这条边可以出现在这张图的某几个最小生成树中,输出at least once;若这条边不会出现在这张图的任意一个最小生成树中,输出none

 

 

/*

 

input

4 5

1 2 101

1 3 100

2 3 2

2 4 2

3 4 1

output

none

any

at least one

at least one

any

*/

 

 

#include <iostream>

#include <cstdio>

#include <cstring>

#include <algorithm>

#define maxn 100010

using namespace std;

 

struct Edge{

    int from,to,dist,id;

}a[maxn];

 

int first[maxn],v[maxn*2],next[maxn*2],id[maxn*2];

int ans[maxn],fa[maxn],pre[maxn],dfs_clock,e;

 

int cmp(Edge a,Edge b){

    return a.dist < b.dist;

}

void init(){

    e = 0;

    memset(first,-1,sizeof(first));

}

 

void add_edge(int a,int b,int ID){

    v[e] = b;

    next[e] = first[a];

    id[e] = ID;

    first[a] = e++;

}

 

int dfs(int u,int fa_id){

    int lowu = pre[u] = ++dfs_clock;

    for(int i = first[u];i != -1;i = next[i]){

        if(!pre[v[i]]){

            int lowv = dfs(v[i],id[i]);

            lowu = min(lowu,lowv);

            if(lowv > pre[u])   ans[id[i]] = 1;

        }else if(pre[v[i]] < pre[u] && id[i] != fa_id){

            lowu = min(lowu,pre[v[i]]);

        }

    }

    return lowu;

}

 

int find(int x){return x == fa[x] ? x : fa[x] = find(fa[x]);}

 

void join(int x,int y){

    int fx = find(x);

    int fy = find(y);

    if(fx != fy){

        e = 0;

        first[fx] = first[fy] = -1;

        pre[fx] = pre[fy] = 0;

        fa[fx] = fy;

    }

}

 

int main()

{

    int n,m;

    scanf("%d%d",&n,&m);

    init();

    for(int i = 1;i <= n;i++)   fa[i] = i;

    dfs_clock = 0;

    for(int i = 0;i < m;i++){

        scanf("%d%d%d",&a[i].from,&a[i].to,&a[i].dist);

        a[i].id = i;

    }

    sort(a,a+m,cmp);

    memset(pre,0,sizeof(pre));

    memset(ans,0,sizeof(ans));

    for(int i = 0;i < m;i++){

        int j = i+1;

        while(j < m && a[i].dist == a[j].dist)    j++;

        for(int k = i;k < j;k++){

            int fx = find(a[k].from);

            int fy = find(a[k].to);

            if(fx != fy){

                add_edge(fx,fy,a[k].id);

                add_edge(fy,fx,a[k].id);

                ans[a[k].id] = 2;

            }

        }

        for(int k = i;k < j;k++){

            int fx = find(a[k].from);

            int fy = find(a[k].to);

            if(fx != fy && !pre[fx]){

                dfs(fx,-1);

            }

        }

        for(int k = i;k < j;k++){

            join(a[k].from,a[k].to);

        }

        i = j - 1;

    }

    for(int i = 0;i < m;i++){

        if(ans[i] == 0) printf("none\n");

        else if(ans[i] == 1)    printf("any\n");

        else    printf("at least one\n");

    }

    return 0;

}

 

 

 

15hdu 3371 最小生成树(有重边)

 

/*

the cities are signed from 1 to n.

1

6( survived cities) 4(you can choose to connect the cities ) 3(still connected cities)

it takes c to connect p and q

p q c

1 4 2

2 6 1

2 3 5

3 4 33

the number of this connected citie(2ge 2ge 3ge)

2 1 2

2 1 3

3 4 5 6

 

Output

For each case, output the least money you need to take, if its impossible, just output -1.

*/

#include<stdio.h>

#include<string.h>

#include<algorithm>

using namespace std;

int n,m,k,p[505],ans;

struct node{

    int u,v,w;

}pt[250000];

int cmp(node a,node b)

{

    return a.w<b.w;

}

int find(int x){return p[x]==x?x:p[x]=find(p[x]);}

int main()

{

    int t;

    scanf("%d",&t);

    while(t--)

    {

        int i,j,num,a,b,root,x,y;

        for(i=0;i<=500;i++) p[i]=i;

        bool flag=false;

        scanf("%d%d%d",&n,&m,&k);

        for(i=1;i<=m;i++)scanf("%d%d%d",&pt[i].u,&pt[i].v,&pt[i].w);

        sort(pt+1,pt+m+1,cmp);

        for(i=1;i<=k;i++)

        {

            scanf("%d%d",&num,&root);

            for(j=1;j<num;j++)

            {

                scanf("%d",&a);

                 x=find(a),y=find(root);

                if(x!=y)  p[x]=y;

            }

        }

        for(num=0,i=1;i<=n;i++) if(p[i]==i) num++;

        for(ans=0,i=1;i<=m;i++)

        {

            x=find(pt[i].u);y=find(pt[i].v);

            if(x!=y)

            {

                p[x]=y;

                ans+=pt[i].w;num--;

            }

            if(num==1)

            {

                flag=true;

                break;

            }

        }

        if(flag) printf("%d\n",ans);

        else printf("-1\n");

    }

    return 0;

}

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值