数据结构第四次实验上机考试

7-1 连通分量 

无向图 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

分析:

1、求连通分量个数,简单点说就是看有几个集合,凡是涉及到分类的问题,我们都可以用并查集解决,找爹和祖宗它不快乐吗,哈哈哈。

2、这道题也可用DFS遍历图节点,然后记录遍历次数,即有几个连通分量,但是这个要小心,容易爆栈。

完整代码:(并查集)

#include<bits/stdc++.h>

using namespace std;

int f[100010];

int fa(int n)
{
    if(f[n]==n)
        return n;
    return f[n]=fa(f[n]);
}

bool he(int x,int y)
{
    int f1=fa(x);
    int f2=fa(y);
    if(f1!=f2)
    {
     f[f2]=f1;
        return true;
    }
    return false;
}

int main()
{
    int n,m,x,y;
    scanf("%d %d",&n,&m);
    int s=n;
    for(int i=1;i<=n;i++)
        f[i]=i;
    for(int i=1;i<=m;i++)
    {
        scanf("%d %d",&x,&y);
        if(he(x,y))
        s--;
    }
    cout<<s;
    return 0;
}

7-2 整数拆分 

整数拆分是一个古老又有趣的问题。请给出将正整数 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

分析:

1、考察回溯法,说白了让你写一个复杂点的DFS,设置三个形参,sum表示当前数字,每次选择一个数字分割都需要减去,比如sum-i。s表示分割了几次,x表示上次分割的数字。当分割次数s=k时,种类数ans++,依次输出f[i]的值,在分割次数没有到达s的时候,给f[i]赋值,递归调用DFS。

void dfs(int sum,int s,int x)
{
    if(s==k&&sum==0)
    {
       ans++;
       for(int i=1;i<=k;i++)
       {
           printf("%d",f[i]);
           if(i!=k)printf(" ");
           else
            printf("\n");
       }
    }
    for(int i=x;i<=sum;i++)
    {
        f[s+1]=i;
        dfs(sum-i,s+1,i);
    }
}

完整代码:

#include<bits/stdc++.h>

using namespace std;

int n,k;
int ans=0;
int f[100];
inline int read()
{
	char c=getchar();int x=0,f=1;
	for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
	for(;isdigit(c);c=getchar())x=x*10+c-48;
	return x*f;
}

void dfs(int sum,int s,int x)
{
    if(s==k&&sum==0)
    {
       ans++;
       for(int i=1;i<=k;i++)
       {
           printf("%d",f[i]);
           if(i!=k)printf(" ");
           else
            printf("\n");
       }
    }
    for(int i=x;i<=sum;i++)
    {
        f[s+1]=i;
        dfs(sum-i,s+1,i);
    }
}

int main()
{
	ios::sync_with_stdio(false);
	n=read();
	k=read();
	if(n<k){
        printf("0");
        return 0;
	}

        dfs(n,0,1);
        printf("%d",ans);
    return 0;
}

7-3 数字变换 

利用变换规则,一个数可以变换成另一个数。变换规则如下:(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

分析:

1、变换规则有三种,凡是有多种选择的题,就是BFS,别管能不能做上,思路没问题,哈哈。

2、设置一个标记数组visit,记录当前节点有没有走过,走过就标记,在以后都不用走了,因为此时就是最短路,如果还有其他方式到达此结点,那么一定比当前距离长。从目标结点出发,依次按规则(1)(2)(3)走,走一步记录一步,依次将经过的结点压入队列中,知道处理的数字增加到y为止,退出循环。此外开一个step数组记录到过哪些结点,记录路径。

inline void bfs()
{
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        if(u==y)
            return;
        if(!visit[u+1]&&u+1<=100000&&u+1>=1)
        {
            visit[u+1]=1;
            q.push(u+1);
            f[u+1]=f[u]+1;
            step[u+1]=u;
        }
        if(!visit[u*2]&&u*2<=100000&&u*2>=1)
        {
            visit[u*2]=1;
            q.push(u*2);
            f[u*2]=f[u]+1;
            step[u*2]=u;
        }
        if(!visit[u-1]&&u-1<=100000&&u-1>=1)
        {
            visit[u-1]=1;
            q.push(u-1);
            f[u-1]=f[u]+1;
            step[u-1]=u;
        }
    }
}

完整代码:

#include<bits/stdc++.h>
#include<queue>

using namespace std;

inline int read()
{
	char c=getchar();int x=0,f=1;
	for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
	for(;isdigit(c);c=getchar())x=x*10+c-48;
	return x*f;
}
//ios::sync_with_stdio(false);

queue<int>q;
int f[100010],x,y,s,ans[100010],step[100010];
int visit[100010];

inline void bfs()
{
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        if(u==y)
            return;
        if(!visit[u+1]&&u+1<=100000&&u+1>=1)
        {
            visit[u+1]=1;
            q.push(u+1);
            f[u+1]=f[u]+1;
            step[u+1]=u;
        }
        if(!visit[u*2]&&u*2<=100000&&u*2>=1)
        {
            visit[u*2]=1;
            q.push(u*2);
            f[u*2]=f[u]+1;
            step[u*2]=u;
        }
        if(!visit[u-1]&&u-1<=100000&&u-1>=1)
        {
            visit[u-1]=1;
            q.push(u-1);
            f[u-1]=f[u]+1;
            step[u-1]=u;
        }
    }
}

int main()
{
    x=read();
    y=read();
    f[x]=0;
    q.push(x);
    step[x]=0;
    visit[x]=1;
    bfs();
    int tmp=y;
    while(tmp)
    {
        ans[++s]=tmp;
        tmp=step[tmp];
    }
    printf("%d\n",f[y]);
    for(int i=f[y];i>=1;i--)
    {
        printf("%d",ans[i]);
        if(i!=1)
            printf(" ");
    }
    return 0;
}

7-4 旅行 I 

五一要到了,来一场说走就走的旅行吧。当然,要关注旅行费用。由于从事计算机专业,你很容易就收集到一些城市之间的交通方式及相关费用。将所有城市编号为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>
#define M(x,y) make_pair(x,y)
using namespace std;
int fr[100010], to[200010], nex[200010], v[200010], tl, num[100010];
long long d[100010];
bool b[100010];
void add(int x, int y, int w) {
     to[++tl] = y; 
     v[tl] = w;
     nex[tl] = fr[x]; 
     fr[x] = tl;
}
priority_queue< pair<long long, int > >q;
int main() {
int n, m, x, y, z, s;
scanf("%d %d %d", &n, &m, &s);
for (int i = 1; i <= m; i++)
{
    scanf("%d %d %d", &x, &y, &z);
    add(x, y, z);
    add(y, x, z);
}
for (int i = 1; i <= n; i++)
    d[i] = 1e10;
    d[s] = 0;
    q.push(M(0, s));
while (!q.empty()) 
{
   int x = q.top().second;
   q.pop();
   if (b[x])
     continue;
   b[x] = 1;
for (int i = fr[x]; i; i = nex[i]) 
{
    int y = to[i], l = v[i];
    if (d[y] > d[x] + l) {
       d[y] = d[x] + l;
       num[y] = num[x] + 1;
       q.push(M(-d[y], y));
     }
    else if (d[y] == d[x] + l) 
   {
       if (num[x] + 1 > num[y])
          num[y] = num[x] + 1;
       q.push(M(-d[y], y));
    }
  }
}
for (int i = 1; i <= n; i++)
{
    printf("%lld", d[i]);
    if (i != n)
    printf(" ");
}
printf("\n");
for (int i = 1; i <= n; i++)
{
    printf("%d", num[i]);
    if (i != n)
    printf(" ");
}
return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值