最小生成树

P3366 【模板】最小生成树

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <algorithm>
 4 using namespace std;
 5 int f[5005],n,m,ans,k;
 6 struct point{
 7     int x,y,z;//x和y之间有一条长度为z的连边 
 8 }p[200005];
 9 bool cmp(const point &a,const point &b){
10     return a.z<b.z;
11 }
12 int father(int x){//返回x的父亲 
13     if(f[x]!=x)f[x]=father(f[x]);//如果还未到祖先(祖先的父亲是自己) 
14     return f[x];//继续递归 
15 }
16 void unionn(int x,int y){
17     int fa=father(x),fb=father(y);
18     if(fa!=fb)f[fa]=fb;//合并集合 
19 } 
20 int main(){
21     scanf("%d%d",&n,&m);
22     for(int i=1;i<=n;i++)f[i]=i;//初始化 
23     for(int i=1;i<=m;i++)scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].z);
24     sort(p+1,p+m+1,cmp);
25     
26     for(int i=1;i<=m;i++){
27         if( father(p[i].x)!=father(p[i].y) ){
28             ans+=p[i].z;//加入这条边 
29             unionn(p[i].x,p[i].y);//加入集合 
30             k++;
31         }
32         if(k==n-1)break;//树只有n-1条边 
33     }
34     printf("%d",ans);
35     return 0;
36 }

 

P1194 买礼物

因为起点有权值,所以ans+=A;

若连边为空,或权值大于A,修改为A;

正常跑一边模板...

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #define M 550
 5 #define N 250005
 6 #define R register
 7 using namespace std;
 8 int A,B;
 9 int f[M],ans,k,kk;
10 struct node{
11     int x,y,v;
12 }p[N];
13 inline int ri(){
14     char c=getchar();int x=0,w=1;
15     while(!isdigit(c)){if(c=='-')w=-1;c=getchar();}
16     while( isdigit(c)){x=(x<<3)+(x<<1)+c-48;c=getchar();}
17     return x*w;
18 }
19 inline bool cmp(const node &a,const node &b){
20     return a.v<b.v;
21 }
22 inline int father(int x){
23     if(f[x]!=x)f[x]=father(f[x]);
24     return f[x];
25 }
26 inline void unionn(int x,int y){
27     int fa=father(x),fb=father(y);
28     if(fa!=fb)f[fa]=fb;
29 }
30 int main(){
31     A=ri(),B=ri();
32     for(R int i=1;i<=B;i++)f[i]=i;
33     for(R int i=1;i<=B;i++)
34         for(R int j=1;j<=B;j++){
35             p[++kk].v=ri();
36             p[kk].x=i,p[kk].y=j;
37             if(!p[kk].v||p[kk].v>A)p[kk].v=A;
38         }
39     sort(p+1,p+kk+1,cmp);
40     for(R int i=1;i<=kk;i++){
41         if(father(p[i].x)!=father(p[i].y)){
42             ans+=p[i].v;
43             unionn(p[i].x,p[i].y);
44             k++;
45         }
46         if(k==B-1)break;
47     }
48     printf("%d",ans+A);
49     return 0;
50 }

 最优布线问题

题目描述

    学校有 n 台计算机,为了方便数据传输,现要将它们用数据线连接起来。两台计算机被连接是指它们时间有数据线连接 。由于计算机所处的位置不同,因此不同的两台计算机的连接费用往往是不同的。 
    当然,如果将任意两台计算机都用数据线连接,费用将是相当庞大的。为了节省费用,我们采用数据的间接传输手段,即一台计算机可以间接 的通过若干台计算机(作为中转 )来实现与另一台计算机的连接 。 
    现在由你负责连接这些计算机,你的任务是使任意两台计算机都连通 (不管是直接的或间接的),且总费用最小。

 

输入格式

    第一行为整数 n (2<=n<=100),表示计算机的数 目。此后的 n 行,每行 n 个整数。第 x+1 行 y 列的整数表示直接连接第 x 台计算机和第 y 台计算机的费用(不大于10000),如果费用为0表示(x,y)之间不通。

输出格式

一个整数,表示最小的连接费用。

样例

样例输入1

3
0 1 2
1 0 1
2 1 0

样例输出1

2

 emm类似的模板题..

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 using namespace std;
 5 int n,z,k,ans;
 6 int f[110];
 7 struct node{
 8     int x,y,v;
 9 }p[10005];
10 inline bool cmp(const node &a,const node &b){
11     return a.v<b.v;
12 }
13 inline int father(int x){
14     if(f[x]!=x)f[x]=father(f[x]);
15     return f[x];
16 }
17 inline void unionn(int a,int b){
18     int fa=father(a),fb=father(b);
19     if(fa!=fb)f[fa]=fb;
20 }
21 int main(){
22     cin>>n;
23     for(int i=1;i<=n;i++)
24         for(int j=1;j<=n;j++){
25             cin>>z;
26             if(z)p[++k].x=i,p[k].y=j,p[k].v=z;        
27         }
28     sort(p+1,p+k+1,cmp),k=0;    
29     for(int i=1;i<=n;i++)f[i]=i;
30     for(int i=1;i<=n*n;i++){
31         if(father(p[i].x)!=father(p[i].y)){
32             ans+=p[i].v;
33             k++;
34             unionn(p[i].x,p[i].y);
35         }
36         if(k==n-1)break;
37     }
38     cout<<ans;
39 }

 

 

 征兵

题目描述

一个国王,他拥有一个国家。最近他因为国库里钱太多了,闲着蛋疼要征集一只部队要保卫国家。他选定了N个女兵(编号0...N-1)和M个男兵(编号0...M-1),但事实上每征集一个兵他就要花10000RMB,即使国库里钱再多也伤不起啊。他发现,某男兵和某女兵之间有某种关系(往正常方面想,一共R种关系),这种关系可以使KING少花一些钱就可以征集到兵,不过国王也知道,在征兵的时候,每一个兵只能使用一种关系来少花钱。这时国王向你求助,问他最少要花多少的钱。

输入格式

第一行:T,一共T组数据。
接下来T组数据,
第一行包括N,M,R
接下来的R行包括Xi,Yi,Vi 表示如果招了第Xi个女兵,再招第Yi个男兵能省Vi元(同样表示如果招了第Yi个男兵,再招第Xi个女兵能也省Vi元)

输出格式

共T行,表示每组数据的最终花费是多少(因为国库里的钱只有2^31-1,所以保证最终花费在maxlongint范围内)

样例

样例输入1

2

5 5 8
4 3 6831
1 3 4583
0 0 6592
0 1 3063
3 3 4975
1 3 2049
4 2 2104
2 2 781

5 5 10
2 4 9820
3 2 6236
3 1 8864
2 4 8326
2 0 5156
2 0 1463
4 1 2439
0 4 4373
3 4 8889
2 4 3133

样例输出1

71071
54223

最大权森林....最短路实现。

我们设想这样一个无向图:在征募某个人a时,如果使用了a和b之间的关系,就连一条a到b的边。不可能存在一个圈,因为题目要求是男女之间的关系,一个圈最少三个人,三个人不可能满足相邻的人异性,n(n>=3)时类似。因此可知这个图是一片森林(两个连通分量,男生和女生分别在一个连通分量中)。反之,如果给定了一个森林,就可以确定征募的顺序。

因此,把人看成顶点、把关系看成边,这个问题就转化为求解无向图的最大权森林问题。最大权森林问题可以通过把所有边的权值取反之后用最小生成树的算法求解。

原文:这里。

男女编号都是从0开始坑了半天..其实只要男生编号输入完 + n 。

前几道题都是敲kruaskal吧..接下来还有prim。

当然。个人码风如下。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 using namespace std;
 5 const int maxn=2e4+5;
 6 int n,m,r;
 7 int f[maxn];
 8 struct node{
 9     int u,v,cost;
10 }p[50005];
11 inline bool cmp(const node &a,const node &b){
12     return a.cost<b.cost;
13 }
14 int father(int x){
15     return f[x]==x ? x : f[x]=father(f[x]) ;
16 }
17 void unionn(int x,int y){
18     int fa=father(x),fb=father(y);
19     if(fa!=fb)f[fa]=fb;
20 }
21 inline void Kruskal(){
22     int ans=0;
23     for(int i=0;i<n+m;i++)f[i]=i;
24     sort(p,p+r,cmp);
25     for(int i=0;i<r;i++){
26         node e=p[i];
27         if(father(e.u)!=father(e.v)){
28             unionn(e.u,e.v);
29             ans+=e.cost;
30         }
31     }
32     printf("%d\n",10000*(n+m)+ans);
33 }
34 int main(){
35     int t,a,b,c;
36     scanf("%d",&t);
37     while(t--){
38         scanf("%d%d%d",&n,&m,&r);
39         for(int i=0;i<r;i++){
40             scanf("%d%d%d",&a,&b,&c);
41             p[i]=(node){a,b+n,-c};
42         }
43         Kruskal();
44     }
45     return 0;
46 }

 P1656 炸铁路

额。据说正解是tarjan。

我并没有学过啊。。。

看着dalao的博客,这题在kruaskal中。

再看了看数据范围。

哦。大力枚举啊。m^2呢。

每次删一条边。然后做一次假的kruaskal,看是否能够成树。也就是是否能连通。

没有边长。排序的技巧就在于满足恶心的输出要求,从小到大。

tarjan。。以后学了再说吧??

所以自己bb了一下假的kruaskal代码。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #define R register
 5 using namespace std;
 6 int n,m,f[200],tot;
 7 struct node{
 8     int x,y;
 9 }p[5010];
10 inline int ri(){
11     char c=getchar();int x=0,w=1;
12     while(!isdigit(c)){if(c=='-')w=-1;c=getchar();}
13     while( isdigit(c)){x=(x<<3)+(x<<1)+c-48;c=getchar();}
14     return x*w;
15 }
16 inline bool cmp(const node &a,const node &b){
17     if(a.x<b.x || (a.x==b.x&&a.y<b.y) )return 1;
18     return 0;
19 }
20 inline int father(int x){
21     return f[x]==x ? x : f[x]=father(f[x]);
22 }
23 inline void unionn(int x,int y){
24     int fa=father(x),fb=father(y);
25     if(fa!=fb)f[fa]=fb,tot++;
26 }
27 int main(){
28     int a,b;
29     n=ri(),m=ri();
30     for(R int i=1;i<=m;++i){
31         a=ri(),b=ri();
32         p[i].x=min(a,b),p[i].y=max(a,b);
33     }
34     sort(p+1,p+m+1,cmp);
35     for(R int i=1;i<=m;++i){//每次删第i条边 
36         tot=0;
37         for(R int j=1;j<=n;++j)f[j]=j;
38         for(R int j=1;j<=m;++j){
39             if(i!=j)unionn(p[j].x,p[j].y);
40             if(tot==n-1)break; 
41         }
42         if(tot!=n-1)printf("%d %d\n",p[i].x,p[i].y);
43     }
44     return 0;
45 } 

 P1195 口袋的天空

求连通块啊。

别方。

这和求最小生成树没什么区别。只是最小生成树要连所有的点,这题只要连通块为k。

kruaskal。

初始,连通块个数为n。

加上一条边,连通块的个数- -。

加到连通块的个数减到k为止。

减不到?输出无解。

要求代价最小,边权排个序。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #define R register 
 5 using namespace std;
 6 int n,m,k,f[1010],ans;
 7 struct node{
 8     int x,y,v;
 9 }p[10005];
10 inline int ri(){
11     char c=getchar();int x=0,w=1;
12     while(!isdigit(c)){if(c=='-')w=-1;c=getchar();}
13     while( isdigit(c)){x=(x<<3)+(x<<1)+c-48;c=getchar();}
14     return x*w;
15 }
16 inline bool cmp(node a,node b){
17     return a.v<b.v;
18 }
19 inline int father(int x){
20     return f[x]==x ? x : f[x]=father(f[x]);
21 }
22 inline void unionn(int x,int y){
23     int fa=father(x),fb=father(y);
24     if(fa!=fb)f[fa]=fb;
25 }
26 int main(){
27     n=ri(),m=ri(),k=ri();
28     int tot=n;
29     for(R int i=1;i<=m;i++)p[i].x=ri(),p[i].y=ri(),p[i].v=ri();
30     for(R int i=1;i<=n;i++)f[i]=i;
31     sort(p+1,p+m+1,cmp);
32     for(R int i=1;i<=m;i++){
33         node e=p[i];
34         if(father(e.x)!=father(e.y)){
35             tot--,ans+=e.v;
36             unionn(e.x,e.y);
37         }
38         if(tot==k){
39             printf("%d",ans);
40             return 0;
41         }
42     }
43     printf("No Answer");
44     return 0;
45 }

 1443 公路修建

题目描述

      某国有n个城市,它们互相之间没有公路相通,因此交通十分不便。为解决这一“行路难”的问题,政府决定修建公路。修建公路的任务由各城市共同完成。
    修建工程分若干轮完成。在每一轮中,每个城市选择一个与它最近的城市,申请修建通往该城市的公路。政府负责审批这些申请以决定是否同意修建。
    政府审批的规则如下:
    (1)如果两个或以上城市申请修建同一条公路,则让它们共同修建;
    (2)如果三个或以上的城市申请修建的公路成环。如下图,A申请修建公路AB,B申请修建公路BC,C申请修建公路CA。则政府将否决其中最短的一条公路的修建申请;


    (3)其他情况的申请一律同意。
    一轮修建结束后,可能会有若干城市可以通过公路直接或间接相连。这些可以互相:连通的城市即组成“城市联盟”。在下一轮修建中,每个“城市联盟”将被看作一个城市,发挥一个城市的作用。
    当所有城市被组合成一个“城市联盟”时,修建工程也就完成了。
    你的任务是根据城市的分布和前面讲到的规则,计算出将要修建的公路总长度。

输入格式

 第一行一个整数n,表示城市的数量。(n≤5000)
   以下n行,每行两个整数x和y,表示一个城市的坐标。(-1000000≤x,y≤1000000)

输出格式

   一个实数,四舍五入保留两位小数,表示公路总长。

样例

样例输入1

4
0 0
1 2
-1 2
0 4

样例输出1

  6.47

5000个点。12497500条边。排序边要超时呀。kruaskal

所以prim。n^2可接受呢。

无比类似于模板了。define用得比较多,为了代码好看。。。

prim的思想就是,首先预处理。

 1 for(R int i=2;i<=n;i++)dis[i]=INF;//到dis[i]的最优距离 

然后第一层for,找与这个点连得最小边。

第二层for,找到后,找每个点与这个当前选中的点的距离,看能否刷新dis。

 

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <cmath>
 5 #define R register
 6 #define MN 5005
 7 #define INF 1e9
 8 #define t work(p[dx][0],p[dx][1],p[j][0],p[j][1])
 9 #define x2 (x-xx)*(x-xx)
10 #define y2 (y-yy)*(y-yy)
11 using namespace std;
12 int p[MN][2];//第i个点坐标:(p[i][0],p[i][1]) 
13 bool uesd[MN];//prim算法,这个点用过没有
14 double dis[MN],ans;//到dis[i]的最优距离
15 inline double work(int x,int y,int xx,int yy){//算距离 
16     return sqrt( (double)x2 + (double)y2 );
17 } 
18 inline int ri(){
19     char c=getchar();int x=0,w=1;
20     while(!isdigit(c)){if(c=='-')w=-1;c=getchar();}
21     while( isdigit(c)){x=(x<<3)+(x<<1)+c-48;c=getchar();}
22     return x*w;
23 }
24 int main(){
25     int n; n=ri();
26     for(R int i=1;i<=n;i++)p[i][0]=ri(),p[i][1]=ri();
27     for(R int i=2;i<=n;i++)dis[i]=INF;//基本操作 设为inf
28     int dx; double dy;//目前加的点,及其边权
29     for(R int i=1;i<=n;i++){ 
30         dy=INF;
31         for(R int j=1;j<=n;j++)
32             if(!uesd[j]&&dis[j]<dy){//找到更短
33                 dx=j, //这个点存下 
34                 dy=dis[j];//记下新的最短值        
35         }
36         ans+=dy;//搜完了,记下最优的答案 
37         uesd[dx]=1;//标记为用过 
38         for(R int j=1;j<=n;j++)//刷新。当前加入的点能到达的每个点,尝试能否刷新他们的dis 
39             if(t<dis[j])dis[j]=t;//t见define 
40     } 
41     printf("%.2lf\n",ans);
42     return 0; 
43 }

 

 

 

 

posted @ 2019-01-06 21:37  flickerr  阅读(257)  评论(0)    收藏  举报