数据结构荣誉课第五次上机实验

数据结构荣誉课第五次上机实验

7-1 图的深度优先搜索I (100 分)

无向图 G 有 n 个顶点和 m 条边。求图G的深度优先搜索树(森林)以及每个顶点的发现时间和完成时间。每个连通分量从编号最小的结点开始搜索,邻接顶点选择顺序遵循边的输入顺序。

在搜索过程中,第一次遇到一个结点,称该结点被发现;一个结点的所有邻接结点都搜索完,该结点的搜索被完成。深度优先搜索维护一个时钟,时钟从0开始计数,结点被搜索发现或完成时,时钟计数增1,然后为当前结点盖上时间戳。一个结点被搜索发现和完成的时间戳分别称为该结点的发现时间和完成时间

输入格式:

第1行,2个整数n和m,用空格分隔,分别表示顶点数和边数, 1≤n≤50000, 1≤m≤100000.

第2到m+1行,每行两个整数u和v,用空格分隔,表示顶点u到顶点v有一条边,u和v是顶点编号,1≤u,v≤n.

输出格式:

第1到n行,每行两个整数di和fi,用空格分隔,表示第i个顶点的发现时间和完成时间1≤i≤n 。

第n+1行,1个整数 k ,表示图的深度优先搜索树(森林)的边数。

第n+2到n+k+1行,每行两个整数u和v,表示深度优先搜索树(森林)的一条边<u,v>,边的输出顺序按 v 结点编号从小到大。

输入样例:

在这里给出一组输入。例如:

6 5
1 3
1 2
2 3
4 5
5 6

输出样例:

在这里给出相应的输出。例如:

1 6
3 4
2 5
7 12
8 11
9 10
4
3 2
1 3
4 5
5 6

 解题思路:设置标记数组visit,按照序号从小到大开始遍历节点,如果该节点visit为0,则以他为根节点遍历连通分量,遍历方法用DFS遍历,遍历过记得打上标记,同时遍历开始更新开始遍历的时间,所有邻接节点遍历过后更新结束时间,每次遍历邻接节点将邻接边保存进pair数组
并且跟新深度搜索树的边数,最后将边按照要求排序输出即可。

完整代码:

#include <bits/stdc++.h>
using namespace std;
vector<int> s[50010];
int n,m;
int Find[50000]={0},finish[50010]={0},visit[50010]={0};
int Time=0;
int bian=0;
pair<int,int>q[100010];
int key=0;
bool compare(pair<int,int>a,pair<int,int> b){
    if(a.second<b.second)
    return true;
    else
    return false;
}
void DFS(int k){
    visit[k]=1;
    Time++;
    Find[k]=Time;
    for(vector<int>::iterator it=s[k].begin();it!=s[k].end();it++){
        if(visit[*it]==0){
            q[key++]=make_pair(k,*it);
            bian++;
            DFS(*it);
        }
    }
    Time++;
    finish[k]=Time;
}
int main(){
int x,y;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
    scanf("%d%d",&x,&y);
    s[x].push_back(y);
    s[y].push_back(x);
}
for(int i=1;i<=n;i++){
    if(visit[i]==0){
        DFS(i);
    }
}
for(int i=1;i<=n;i++){
    printf("%d %d\n",Find[i],finish[i]);
}
printf("%d\n",bian);
sort(q,q+bian,compare);
for(int i=0;i<bian;i++)
printf("%d %d\n",q[i].first,q[i].second);
}

7-2 圆 (100 分)

二维平面上有n 个圆。请统计:这些圆形成的不同的块的数目。

圆形成的块定义如下: (1)一个圆是一个块; (2)若两个块有公共部分(含相切),则这两个块形成一个新的块,否则还是两个不同的块。

输入格式:

第1行包括一个整数n,表示圆的数目,n<=8000。

第2到n+1行,每行3 个用空格隔开的数x,y,r。(x,y)是圆心坐标,r 是半径。所有的坐标及半径都是不大于30000 的非负整数。

输出格式:

1个整数,表示形成的块的数目。

输入样例:

在这里给出一组输入。例如:

2
0 0 1
1 0 2

输出样例:

在这里给出相应的输出。例如:

1

解题思路: 

 将圆看成一个点,如果两个圆相交或者相切,则认为两点之间有边,最后块的数量其实就等于

 点构成图连通分量的数量,可以采用并查集。注意计算圆心间的距离时注意数据类型使用,

srqt用平方代替,这样精度更高,同时计算机几何问题时尽量避免小数的运算。

完整代码:

#include <bits/stdc++.h>
using namespace std;
int n;
int x[8010]={0},y[8010]={0},r[8010]={0};
int father[8010];
int num=0;
int Find(int m){
    if(father[m]==m)return m;
    else
    return father[m]=Find(father[m]);
}
void combine(int l,int e){
    int x=Find(l);
    int y=Find(e);
    if(x==y)return ;
    else father[y]=x;
}
int dis(int x1,int y1,int r1,int x2,int y2,int r2){
    if(sqrt(((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)))<=r1+r2)
    return 0;
    else
    return 1;
}
int main(){
    int a,b,c;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d%d",&x[i],&y[i],&r[i]);
    }
    for(int i=1;i<=n;i++)
    father[i]=i;
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            int o=dis(x[i],y[i],r[i],x[j],y[j],r[j]);
            if(o==0)
            combine(i,j);
        }
    }
    for(int i=1;i<=n;i++) {
        if(father[i]==i)
        num++;
    }
    printf("%d",num);
}

7-3 供电 (100 分)

要给N个地区供电。每个地区或者建一个供电站,或者修一条线道连接到其它有电的地区。试确定给N个地区都供上电的最小费用。

输入格式:

第1行,两个个整数 N 和 M , 用空格分隔,分别表示地区数和修线路的方案数,1≤N≤10000,0≤M≤50000。

第2行,包含N个用空格分隔的整数P[i],表示在第i个地区建一个供电站的代价,1 ≤P[i]≤ 100,000,1≤i≤N 。

接下来M行,每行3个整数a、b和c,用空格分隔,表示在地区a和b之间修一条线路的代价为c,1 ≤ c ≤ 100,000,1≤a,b≤N 。

输出格式:

一行,包含一个整数, 表示所求最小代价。

输入样例:

在这里给出一组输入。例如:

4 6
5 4 4 3
1 2 2
1 3 2
1 4 2
2 3 3
2 4 3
3 4 4

输出样例:

在这里给出相应的输出。例如:

9

解题思路:

这道题很显然是最小生成树问题,这道题比较特殊的是可以建立一个发电而不是连接两个城市,因为有可能建立发电站的代价更小,所以一个连通分量可能不仅仅只是根节点有一个发电站,还有就是一定要保证最后至少有一个发电站,也就是需要确定最小生成树的开始节点,这里选建立代价最小的节点,分两种算法解答:

prim:

首先将lowcost赋值为建立发电站的代价,因为如果建立发电站代价更小的话可以及时更新,生成节点的所有邻接边,然后采用prim算法即可。

 #include <bits/stdc++.h>
using namespace std;
int n,m;
int p[10010];
const int INF=0x3f3f3f;
int Min=1;
int sum=0;
int lowcost[10010],mark[10010];
vector<pair<int,int> >s[10010];
int main(){
    int x,y,z;
    int u;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&p[i]);
    }
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&x,&y,&z);
        s[x].push_back(make_pair(y,z));
        s[y].push_back(make_pair(x,z));
    }
    for(int i=1;i<=n;i++){
        lowcost[i]=p[i];
        mark[i]=0;
    }
    for(int i=1;i<=n;i++){
        if(p[i]<p[Min])Min=i;
    }
    mark[Min]=1;lowcost[Min]=p[Min];
    for(vector<pair<int,int> >::iterator it=s[Min].begin();it!=s[Min].end();it++){
        if(it->second<lowcost[it->first]){
            lowcost[it->first]=it->second;
        }
    }
    for(int j=1;j<n;j++){
        int dis=INF;
        for(int i=1;i<=n;i++){
            if(lowcost[i]<dis&&mark[i]==0){
            dis=lowcost[i];
            u=i;
        }
        }
    mark[u]=1;
    for(vector<pair<int,int> >::iterator it=s[u].begin();it!=s[u].end();it++){
        if(it->second<lowcost[it->first]&&mark[it->first]==0)
        lowcost[it->first]=it->second;
    }
}
    for(int i=1;i<=n;i++){
        sum+=lowcost[i];
    }
printf("%d",sum);
}

Kruskal算法:

取最小边用排序实现,判环用并查集实现。

#include <bits/stdc++.h>
using namespace std;
int n,m;
typedef struct toys{
    int x;
    int y;
    int cost;
}T;
int sum=0;
int father[10010];
int p[10010];
T key[100010];
bool compare(T a,T b){
    return a.cost<b.cost;
}
int Find(int a){
    if(father[a]==a)return a;
    else return father[a]=Find(father[a]);
}
void combine(int x,int y){
    int a=Find(x);
    int b=Find(y);
    if(a!=b) father[b]=a;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&p[i]);
for(int i=1;i<=m;i++){
    scanf("%d%d%d",&key[i].x,&key[i].y,&key[i].cost);
}
int i=m+1;
int k=1;
while(i<=m+n){
    key[i].x=0;
    key[i].y=k;
    key[i].cost=p[k];
    k++;
    i++;
}
sort(key+1,key+m+n+1,compare);
for(int i=1;i<=n;i++)
father[i]=i;
for(int i=1,k=1;k<=n;i++){
    if(Find(key[i].x)!=Find(key[i].y)){
    combine(key[i].x,key[i].y);
    sum+=key[i].cost;
    k++;
    }
}
printf("%d",sum);
}

完整代码:

7-4 发红包 (100 分)

新年到了,公司要给员工发红包。员工们会比较获得的红包,有些员工会有钱数的要求,例如,c1的红包钱数要比c2的多。每个员工的红包钱数至少要发888元,这是一个幸运数字。

公司想满足所有员工的要求,同时也要花钱最少,请你帮助计算。

输入格式:

第1行,两个整数n和m(n<=10000,m<=20000),用空格分隔,分别代表员工数和要求数。

接下来m行,每行两个整数c1和c2,用空格分隔,表示员工c1的红包钱数要比c2多,员工的编号1~n 。

输出格式:

一个整数,表示公司发的最少钱数。如果公司不能满足所有员工的需求,输出-1.

输入样例:

在这里给出一组输入。例如:

 2 1 
 1 2

输出样例:

在这里给出相应的输出。例如:

1777

 解题思路:

因为员工彼此的红包金额有限制,分析题可知是拓扑排序问题,无法满足所有人的要求判断是否有环路即可,注意统计每个人的出度和入度:

#include <bits/stdc++.h>
using namespace std;
int n,m;
vector<int> s[10010];
int Count[10010];
int money[10010];
int flag=0;
int sum=0;
int num=0;
bool Toys(){
    queue<int>q;
    for(int i=1;i<=n;i++){
        if(!Count[i])
        q.push(i);
    }
    while(!q.empty()){
        int k=q.front();
        q.pop();
        num++;
        for(vector<int>::iterator it=s[k].begin();it!=s[k].end();it++){
            money[*it]=max(money[*it],money[k]+1);
            Count[*it]--;
            if(Count[*it]==0)q.push(*it);
        }
    }
    return num==n;
}
int main(){
    int x,y;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        s[y].push_back(x);
        Count[x]++;
    }
    for(int i=1;i<=n;i++) 
    money[i]=888;
    if(Toys()){
        for(int i=1;i<=n;i++) {
        sum+=money[i];
    }
    printf("%d",sum);
    }else{
        printf("-1");
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
对于《数据结构与算法第五版》的上机实验指导,我很抱歉我不能提供具体的指导内容。但是,我可以给你一些关于数据结构与算法实验的一般指导,希望对你有所帮助。 1. 确定实验目标:首先,你需要明确每个实验的目标和要求。这包括实验要解决的问题、要使用的数据结构和算法、预期的实验结果等。 2. 设计合适的数据结构:根据实验要求,选择合适的数据结构来解决问题。例如,可以使用链表、树、图等数据结构来组织数据,以便进行相应的操作。 3. 实现算法:根据实验要求和选择的数据结构,编写相应的算法来解决问题。这包括对数据结构进行插入、删除、查找等操作的具体实现。 4. 编写测试代码:为了验证你的实现是否正确,编写测试代码来模拟各种情况下的输入和输出。通过测试代码,可以检查你的算法是否满足预期要求,并找出可能存在的问题。 5. 运行和调试:运行你的程序并进行调试。如果发现程序有错误或不符合预期结果,可以通过调试工具和打印中间结果等方式来找出问题所在,并进行修复。 6. 总结和分析结果:对实验结果进行总结和分析。比较实验结果与预期结果的差异,思考实验中遇到的问题以及可能的改进方法。 总之,进行数据结构与算法实验需要你具备一定的编程能力和理解能力。同时,根据实验要求,选择合适的数据结构和算法,并进行适当的测试和调试。希望以上指导对你有所帮助!如果你有具体的问题,欢迎继续提问。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值