数据结构第四次解题报告

数据结构第四次解题报告

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
输出样例:
在这里给出相应的输出。例如:

2
在这里插入图片描述
解答:如果两个点之间有路径说明两个点在一个连通分量中,这是一个把两点之间有路径的点分在一起的分类问题,可以使用并查集,使用并查集的时候用按秩合并和路径压缩可以大大减小时间复杂度。可以使用dfs遍历每一个连通分量的点,记录遍历次数。
具体代码如下:

#include<bits/stdc++.h>
using namespace std;
int father[50010];
int num;
int find(int v)
{
	if(father[v]<=0)return v;
	return father[v]=find(father[v]);//路径压缩 
}
void UNION(int x,int y)//按秩合并 
{
	int fx=find(x),fy=find(y);
	if(fx==fy)return;
	num--;
	if(father[fx]<father[fy])
	{
	father[fy]=fx;
	father[fx]--;
	}
    else 
	{
		if(father[fx]==father[fy])
		father[fy]--;
		father[fx]=fy;
	}
}
int main()
{
	int n,m,i,j;
	cin>>n>>m;
	num=n;
	int u,v;
	for(i=0;i<n;i++)
	father[i]=-1;
	for(i=0;i<m;i++)
	{
	cin>>u>>v;
	UNION(u,v);	
	}
	cout<<num;
 } 


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

在这里插入图片描述
解答:
此题使用DFS和回溯法,感觉和n皇后问题有些相似,先分好之前的数,再一个个向下分解。
具体代码如下:

#include<bits/stdc++.h>
using namespace std;
int a[52];
int num,n,k;
void dfs(int y,int x)
{
	int i;
	if(y==0&&x!=k+1)return;
	if(y==0&&x==k+1)
	{
		num++;
		for(i=1;i<=k;i++)
		{
			cout<<a[i];
			if(i!=k)cout<<" ";
			if(i==k)cout<<"\n";
		}
		return;
	}
	for(i=a[x-1];i<=y;i++)
	{
		a[x]=i;
		dfs(y-i,x+1);
	}
}
int main()
{
	int i;
	cin>>n>>k;
	for(i=1;i<=n;i++)
	{
		a[1]=i;
		dfs(n-i,2);
	}
	cout<<num;
}

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

在这里插入图片描述
解答:
寻找从x到y的最短路径,由一个值可以扩展出三个值,我们可以一层一层的搜索,使用队列实现的BFS将当前这一层处理完再处理下一层。使用一个数组把数字的的值当作下标,起始时初始值为-1,如果2*n或者n-1或者n+1的数组值不为-1将将其出入队,相当于一个visited数组,并把数组的值修改为n,这样就可以从a[y]一直逆着寻找路径。
具体代码如下:

#include<bits/stdc++.h>
using namespace std;
queue<int>que;
int a[200010];
int b[100000];
int main()
{
	int x,y,s,max;
	cin>>x>>y;
	for(int i=0;i<=100010;i++)
	a[i]=-1;
    a[x]=x;
	que.push(x);
	int n=0,i,j;
	while(n!=y)
	{
	n=que.front();	
	que.pop();
	if(n==y)break;
	if(n+1>=1&&n+1<=100010)
	{
		if(a[n+1]==-1)
		{
    	que.push(n+1);
    	a[n+1]=n;
		}
		
	}
	if(2*n>=1&&2*n<=100010)
	{
		if(a[2*n]==-1)
		{
		que.push(2*n);
    	a[2*n]=n;	
		}
		
	}
	if(n-1>=1&&n-1<=100010)
	{
	if(a[n-1]==-1)
	{
	que.push(n-1);
	a[n-1]=n;	
	}	
	}
}
	int num=0;
	i=y;
	while(i!=x)
	{
		b[num++]=i;
		i=a[i];
	}
	cout<<num<<endl;
	for(i=num-1;i>=0;i--)
	{
		cout<<b[i];
		if(i!=0)cout<<" ";
		else cout<<endl;
	}
}

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算法,用vector存邻接表,dist数组记录长度,path记录经过的城市数。
具体代码如下:

#include<bits/stdc++.h>
using namespace std;
vector<int>p[10100];
vector<int>cost[10010];
int path[10010],dist[10010];
int visited[10010];
int a[10010];
int n,m,s;
void DJS(int v)
{
	int k,j,i,mindist;
	for(i=1;i<=n;i++)
	{
		path[i]=-1,dist[i]=1000000;
	}
	path[v]=0;
	dist[v]=0;
	for(j=1;j<n;j++)
	{
		mindist=1000000;
		for(i=1;i<=n;i++)
		{
			if(dist[i]<mindist&&visited[i]==0)
			{
				mindist=dist[i];v=i;
			}
		}
		visited[v]=1;
		for(i=0;p[v][i]!=-1;i++)
		{
			k=p[v][i];
			if(dist[v]+cost[v][i]<dist[p[v][i]]||dist[v]+cost[v][i]==dist[p[v][i]]&&path[p[v][i]]<path[v]+1) 
			{
				dist[p[v][i]]=dist[v]+cost[v][i];
				path[p[v][i]]=path[v]+1;
			}
		}
	}
}
int main()
{
	scanf("%d%d%d",&n,&m,&s);
	dist[s]=0;
	int i,u,v,w;
	for(i=1;i<=m;i++)
	{
		cin>>u>>v>>w;
		p[u].push_back(v);
		cost[u].push_back(w);
		p[v].push_back(u);
		cost[v].push_back(w);
	}
		for(i=0;i<=n;i++)
	visited[i]=0;
	for(i=1;i<=n;i++)
	{
		p[i].push_back(-1);
	}
	DJS(s);
	for(i=1;i<=n;i++)
	{
		printf("%d",dist[i]);
		if(i!=n)printf(" ");
	}
	int k=0,j;
	printf("\n");
	for(i=1;i<=n;i++)
	{
		printf("%d",path[i]);
		if(i!=n)printf(" ");
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值