数据结构第四次解题报告
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(" ");
}
}