数据结构第四次上机实验

7-1 连通分量 (100 分)

无向图 G 有 n 个顶点和 m 条边。求 G 的连通分量的数目。

输入格式:

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

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

输出格式:

1行,1个整数,表示所求连通分量的数目。

输入样例:

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

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

输出样例:

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

题目分析

这道题我首先想到的就是并查集,当然用dfs或bfs也可以求出连通分量。

并查集即为集合设置一个标志元素,每次输入边,就将两个连通的顶点的标志元素置为相同的元素,所有边读入之后,存在多少个不同的标志元素,即存在多少个连通分支。

代码实现如下(并查集)

#include<stdio.h>
int a[50000];
int main(){
    int n,i,s,l,m;
    scanf("%d %d",&n,&m);
    for(i=1;i<=n;i++){
        a[i]=i;
    }
    for(i=1;i<=m;i++){
        scanf("%d %d",&s,&l);
            int t=l,o=s;
            while(a[o]!=o)o=a[o];
            while(a[t]!=t)t=a[t];
            if(t==o)continue;
            else a[o]=t;
    }
    int sum=0; 
    for(i=1;i<=n;i++){
        int h=i;
        //while(h!=a[h])h=a[h];
        if(a[i]==i){sum++;}
    }
    printf("%d",sum);
    return 0;
}

7-2 整数拆分 (100 分)

整数拆分是一个古老又有趣的问题。请给出将正整数 n 拆分成 k 个正整数的所有不重复方案。例如,将 5 拆分成 2 个正整数的不重复方案,有如下2组:(1,4)和(2,3)。注意(1,4) 和(4,1)被视为同一方案。每种方案按递增序输出,所有方案按方案递增序输出。

输入格式:

1行,2个整数n和k,用空格分隔, 1≤k≤n≤50.

输出格式:

若干行,每行一个拆分方案,方案中的数用空格分隔。

最后一行,给出不同拆分方案的总数。

输入样例:

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

5 2

输出样例:

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

1 4
2 3
2

题目分析

即考察回溯法的运用,只需注意运算过程中,保持序列不减即可。

代码实现如下

#include <iostream>
using namespace std;
int N,K;
int ans[100];
int sum;
void solve(int n,int k){
    if(k==K){ 
        if(n) return ;//如果还有未分配的数则不合法
        for(int i=1;i<=K;i++){
            cout<<ans[i];
            if(i!=K)cout<<" ";
        }   
        cout<<endl;sum++; return ;
    }
    for(int i=ans[k];i<=n-(K-k-1)*ans[k];i++){//i的理想最大值即满足后续所有位
                                              //都置等于它的初始值
        ans[k+1] = i;
        solve(n-i,k+1);
    }
}
int main(){ 
  cin>>N>>K;
  ans[0] = 1;   
  solve(N,0);
  cout<<sum;
  return 0;   
} 

7-3 数字变换 (100 分)

利用变换规则,一个数可以变换成另一个数。变换规则如下:(1)x 变为x+1;(2)x 变为2x;(3)x 变为 x-1。给定两个数x 和 y,至少经过几步变换能让 x 变换成 y.

输入格式:

1行,2个整数x和y,用空格分隔, 1≤x,y≤100000.

输出格式:

第1行,1个整数s,表示变换的最小步数。

第2行,s个数,用空格分隔,表示最少变换时每步变换的结果。规则使用优先级顺序: (1),(2),(3)。

输入样例:

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

2 14

输出样例:

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

4
3 6 7 14

题目分析

即考察bfs的运用,使用一个pair<int,int>存取元素,使用队列以实现广度优先,置一个vis数组,作为访问标志,若某值访问过则不再访问。

代码实现如下

#include <bits/stdc++.h>
using namespace std;

int vis[100010];
int path[100010];
int a[100010];
int fun(int v,int o){
	if(o==1)return v+1;
	else if(o==2)return v*2;
	else if(o==3)return v-1;
}
int sum;
int main(){
	int x,y;
	scanf("%d %d",&x,&y);
	queue<pair<int,int> > q;
	q.push({x,0});
	vis[x]=1;
	path[x]=x;
	while(!q.empty()){
		pair<int,int> t=q.front();
		q.pop();
		if(t.first==y){sum=t.second;break;}
		for(int i=1;i<=3;i++){
			int k=fun(t.first,i);
			if(k<100010&&!vis[k]){
				vis[k]=1;
				path[k]=t.first;
				q.push({k,t.second+1});
			}
			
		}
		
	}
	printf("%d\n",sum);
	int h=y,l=0;
	while(h!=x){a[l++]=h;h=path[h];}
	l--;
	for(;l>=0;l--)
	printf("%d%c",a[l],l==0? '\n':' ');
	return 0; 
}

有趣的是,这道题有一个未解的不成问题的问题,即将vis数组与path数组声明位置上下颠倒,会产生段错误,欢迎大神前来解答。

7-4 旅行 I (100 分)

五一要到了,来一场说走就走的旅行吧。当然,要关注旅行费用。由于从事计算机专业,你很容易就收集到一些城市之间的交通方式及相关费用。将所有城市编号为1到n,你出发的城市编号是s。你想知道,到其它城市的最小费用分别是多少。如果可能,你想途中多旅行一些城市,在最小费用情况下,到各个城市的途中最多能经过多少城市。

输入格式:

第1行,3个整数n、m、s,用空格分隔,分别表示城市数、交通方式总数、出发城市编号, 1≤s≤n≤10000, 1≤m≤100000 。

第2到m+1行,每行三个整数u、v和w,用空格分隔,表示城市u和城市v的一种双向交通方式费用为w , 1≤w≤10000。

输出格式:

第1行,若干个整数Pi,用空格分隔,Pi表示s能到达的城市i的最小费用,1≤i≤n,按城市号递增顺序。

第2行,若干个整数Ci,Ci表示在最小费用情况下,s到城市i的最多经过的城市数,1≤i≤n,按城市号递增顺序。

输入样例:

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

5 5 1
1 2 2
1 4 5
2 3 4
3 5 7
4 5 8

输出样例:

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

0 2 6 5 13
0 1 2 1 3

即考察单源最短路(dijkstra)。值得注意的是,题目要求当距离相同时,要求选择经过点更多的一条路。

代码实现如下:

#include <bits/stdc++.h>
using namespace std;
vector<pair<int,long> > e[10010];
int path[10010];
long dis[10010];
long a[10010];
int num[10010];
void dij(int st,long* dis,int *path,vector<pair<int,long> > *e){
    //long g=sizeof(a);
    //printf("%ld\n",g);
	//memset(dis,-1,g);
    dis[st]=0;
    int i;
    priority_queue <pair<long,int>,vector<pair<long,int> >,greater<pair<long,int> > >q;
    q.push({0,st});
    path[st]=st; 
    while(!q.empty()){
        pair<long,int> du=q.top();q.pop();
        long d=du.first;
        int u=du.second;
        //printf("%ld && %ld && %d  $$ %d\n",d,dis[u],u,e[u].size());
        if(d>dis[u])continue;
        for(i=0;i<e[u].size();i++){
            pair<int,long> vw=e[u][i];
            long w=vw.second;
            int v=vw.first;
            //printf("%ld %d** %ld\n",w,v,dis[v]);
            if(dis[v]==-1||d+w<dis[v]||(d+w==dis[v]&&num[u]>num[path[v]])){
                path[v]=u;
                int l=v,k=0;
                while(path[l]!=l){l=path[l];k++;}
                num[v]=k;
                dis[v]=d+w;
                //printf("%d ",dis[v]);
                q.push({dis[v],v});
            }
        }
    }
    return ;
}
int main(){
    int n,m,s,i;
    scanf("%d %d %d",&n,&m,&s);
    for(i=0;i<m;i++){
        int x,y,w;
        scanf("%d %d %d",&x,&y,&w);
        e[x].push_back({y,w});
        e[y].push_back({x,w});
    }
    //long g=sizeof(dis);
	//printf("%ld\n",g);
    memset(dis,-1,sizeof(dis));
    //long *l=dis;
    dij(s,dis,path,e);
    //printf("\n");
    for(i=1;i<n;i++){
        //int t=i,k=0;
        //while(path[t]!=t){t=path[t];k++;}
        printf("%ld ",dis[i]);
        //num[i]=k;
    }
    //int t=n,k=0;
    //while(path[t]!=t){t=path[t];k++;}
    num[s]=0;
    printf("%d\n",dis[n]);
    for(i=1;i<n;i++){
        printf("%d ",num[i]);
    }
    printf("%d",num[n]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值