作业

以下均属于最小生成树系列=< =

No.1 最优布线问题

描述
学校需要将n台计算机连接起来,不同的2台计算机之间的连接费用可能是不同的。为了节省费用,我们考虑采用间接数据传输结束,就是一台计算机可以间接地通过其他计算机实现和另外一台计算机连接。

为了使得任意两台计算机之间都是连通的(不管是直接还是间接的),需要在若干台计算机之间用网线直接连接,现在想使得总的连接费用最省,让你编程计算这个最小的费用。

输入
输入第一行为两个整数n(2<=n<=100),表示计算机总数.
此后n行,每行n个整数。第x+1行y列的整数表示连接第x太计算机和第y台计算机的费用
输出
输出只有一行一个整数,表示最省的总连接费用。
样例输入
3
0 1 2
1 0 1
2 1 0
样例输出
2

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
这题可以用Prim和Kruskal来做。后者更容易理解,前者更容易写出代码= =Prim就是随意以一个点开始,嗯,随意,每次都走权值最小的,走过后就标记,全标记完了就退出。Kruskal就是以权值大小排序,直接选最小的,贪心,但不要有环出现,这时就需要用到并查集了,记得要压缩= =。哦,对了,Kruskal算法适用于边稀疏的情形,而Prim算法适用于边稠密的情形= =。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
放代码了~、Prim(算答案吧= =)

#include<stdio.h>
int BJ[101],G[101][101];
int main(){
    int n,i,j;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            scanf("%d",&G[i][j]);
    int t=0,S=0,p=n;
    BJ[1]=1;
    for(p=n;p>1;p--){
        int min=999999;
        for(j=1;j<=n;j++){
            if(BJ[j]==1){
               for(i=1;i<=n;i++){
                 if(BJ[i]==0&&G[j][i]!=0&&G[j][i]<min){
                    min=G[j][i];
                    t=i;
                 }
               }  
            }
        }
        //printf("%d\n",min);
        S=S+min;
        BJ[t]=1;
        //printf("%d\n",BJ[t]);
    }
    printf("%d",S);
}
Kruskal(非答案哦= =)
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define MAXN_E 100000
#define MAXN_V 100000
using namespace std;
struct Edge{
    int fm,to,dist;
}e[MAXN_E];
int fa[MAXN_V],n,m;
bool cmp(Edge a,Edge b){
    return a.dist<b.dist;
}
int getfa(int x){
    if(fa[x]==x) return fa[x];
    else return fa[x] = getfa(fa[x]);
}
int same(int x,int y){
    return getfa(x)==getfa(y);
}
void merge(int x,int y){
    int fax=getfa(x),fay=getfa(y);
    fa[fax]=fay;
}
int main(){
    scanf("%d%d",&n,&m);//n为点数,m为边数
    for(int i=1;i<=m;i++)
    scanf("%d%d%d",&e[i].fm,&e[i].to,&e[i].dist);//边集数组存放边
    sort(e+1,e+m+1,cmp);
    for(int i=1;i<=n;i++)
    fa[i]=i;
    int rst=n,ans=0;
    for(int i=1;i<=m && rst>1;i++){
        int x=e[i].fm,y=e[i].to;
        if(same(x,y)) continue;
        else{
            merge(x,y);
            rst--;
            ans+=e[i].dist;
        }
    }
    printf("%d",ans);
}

No.2 联络员

tyvj已经一岁了,网站也由最初的几个用户增加到了上万个用户,随着Tyvi网站的逐步壮大,管理员的数目也越来越多,现在你身为Tyvj管理层的联络员,希望你找到一些通信渠道,使得管理员两两都可以联络(直接或者间接都可以)。Tyvj是一个公益性的网站,没有过多利润,所以你要尽可能地使费用少才可以。

目前你已经知道,Tyvi的通信渠道分为两大类,一类是必选通信渠道,无论价格多少,你都需要把所有的都选择上;还有一类是选择性的通信渠道,你可以从中挑选一些作为最终管理员联络的通信渠道。数据保证给出的通行渠道可以让所有的管理员连通。

输入
第1行:n、m表示Tyvi一共有n个管理员,有m个通信渠道。

第2行到m+1行:每行四个非负整数p,u,v,w,当p=1时,表示这个通信渠道为必选通信渠道;;当p=2时,表示这个通信渠道为选择性通信渠道;u,v,w表示本条信息描述的是u、v管理员之间的通信渠道,u可以收到v的信息,v也可以收到u的信息,w表示费用。

输出
最小的通信费用。

样例输入
5 6
1 1 2 1
1 2 3 1
1 3 4 1
1 4 1 1
2 2 5 10
2 2 5 5
样例输出
9

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(TYVJ已经炸了(>~<)9)
这题同上。稍微改一改就能过了= =。真的。P为1的直接加,然后标记,P为2的就用上面说到的方法就好了= =。(代码自己来~)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

No.3 POJ 1251 Jungle Roads

Lagrishan的一个热带岛屿上的行政长官有一个问题要解决。他决定把几年前得到的外国援助资金用于修建村庄之间的道路。但是丛林比道路多太多了,使道路网络的维护太过于昂贵了。理事会必须选择停止维修一些道路。上述左侧图显示当前所有使用中的道路,以及现在每月的维护费用。当然,村庄之间必需有一些公路能够相通,即使路线并不像以前一样短。行政长官想告诉理事会怎样才使每月的花费最小,并且所维持的道路,将连接所有村庄。上面的地图标记了村庄A到I。右边的图显示了每月能够维护道路的最小费用为216aacms。你的任务是编写一个程序,将解决这些问题。
输入:
输入包含的数据集个数在100以内,以0作为最后一行。每个数据集的第一行只包含一个表示村庄个数的数n,1

#include <iostream>
#include <string.h>
using namespace std;
int road[100][100];
int dist[100];
bool visit[100];
char dic[]=" ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int position(char s){
    int pos;
    for(pos=1;;pos++)
        if(dic[pos]==s)
            return pos;
}
int main(){
    int n,s,e,sum;
    char name;
    while(cin>>n&&n!=0){
        memset(visit,0,sizeof(visit));
        int i,j,m,d;
        for(i=1;i<=n;i++){
            for(j=1;j<=n;j++)
            road[i][j]=10000;
            road[i][i]=0;
        }
            for(i=1;i<n;i++){
                cin>>name>>m;
                s=position(name);
                while(m--){
                    cin>>name>>d;
                    e=position(name);
                    road[s][e]=d;
                    road[e][s]=d;
                }
            }
            for(i=1;i<=n;i++)
            dist[i]=road[1][i];
            visit[1]=1;
            sum=0;
            for(i=1;i<n;i++){
                int max=10000;
                int rec=-1;
                for(j=1;j<=n;j++)
                    if(!visit[j]&&dist[j]<max){
                        max=dist[j];
                        rec=j;
                    }
                    sum+=dist[rec];
                    visit[rec]=1;
                    for(j=1;j<=n;j++)
                        if(!visit[j]&&road[rec][j]<dist[j])
                            dist[j]=road[rec][j];
            }
            cout<<sum<<endl;
    }
    return 1;
}

No.4 连接格点

题目描述
有一个M行N列的点阵,相邻两点可以相连。一条纵向的连线花费一个单位,一条横向的连线花费两个单位。某些点之间已经有连线了,试问至少还需要花费多少个单位才能使所有的点全部连通?

输入
第1行:输入两个正整数m和n。

以下若干行每行四个正整数x1、y1、x2、y2,表示第x1行第y1列的点和第x2行第y2列的点已经有连线。输入保证|x1-x2|+|y1-y2|=1.

输出
输出使得连通所有点还需要的最小花费。

样例输入
2 2
1 1 2 1
样例输出
3
提示
数据范围:
30%的数据满足:n*m<=1000;
100%的数据满足:m,n<=1000.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
这题对了大半就是不全对0(///^///)9。。。。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
借了某人的,不过貌似也不对。

#include<cstdio>
#include<iostream>
using namespace std;
int x1,x2,y1,y2,k,father[1000002],n,m,ans;
int f(int x,int y)
{
    return n*(x-1)+y;   
}
int find(int x)
{
    if (father[x]!=x) father[x]=find(father[x]);
    return father[x];
}
main()
{
    scanf("%d%d",&m,&n);
    for (int i=1;i<=m*n;i++)
    father[i]=i;
    while (cin>>x1>>y1>>x2>>y2)
    {
        int p=find(f(x1,y1)),q=find(f(x2,y2));
        if (p!=q) {father[p]=q;k++;}
    }
    for (int i=1;i<=n;i++)
    for (int j=1;j<m;j++)
    {
        if (k==n*m-1) break;
        int x,y;
        x=find(f(j,i));y=find(f(j+1,i));
        if (x!=y){father[x]=y;k++;ans++;}
    }
    for (int j=1;j<=m;j++)
    for (int i=1;i<n;i++)
    {
        if (k==n*m-1) break;
        int x,y;
        x=find(f(j,i));y=find(f(j,i+1));
        if (x!=y){father[x]=y;k++;ans+=2;}
    }
    printf("%d",ans);
}1

此题摘抄http://blog.csdn.net/xym_csdn/article/details/48915053

改了好多次都没过得了= =,看到别人的,还是错的= =,这题有毒,求大神带飞 0(///^///)9 .

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值