2021-05-28

JLU数据结构荣誉课——第三次上机实验

7-1 连通分量 (100 分)

在这里插入图片描述

题目分析

这道题用dfs或者bfs都行,每次dfs或者bfs能遍历一个联通分量,在遍历的时候计数就能解决。但当时我看到题首先想到了并查集 。可能是我对bfs和dfs的印象不深刻

并查集呢,先将每个元素的所在集合的标志元素取出,进行排序后统计不同标记元素的个数,即为集合的个数,有多少个集合,就有多少个连通分量。

代码实现如下(并查集)

#include<bits/stdc++.h>
#include<stdio.h>
using namespace std;
int a[100001];
int M[100001];
int Find(int x)
{
    if(a[x]==x)return x;
    return a[x]=Find(a[x]);
}
int main()
{
    int i,n,m,t,z=1;
    scanf("%d%d",&n,&m);
    for(i=0;i<=n;i++)
        a[i]=i;
    for(i=0;i<m;i++)
    {
        int p,q;
        scanf("%d%d",&p,&q);
        if(Find(p)!=Find(q))
        {
            int b=Find(q),c=Find(p);
            a[c]=b;
        }
    }


    for(i=1;i<=n;i++)
    {
        M[i]=Find(i);
    }
    sort(M+1,M+n+1);
    for(i=1;i<n;i++)
    {
        if(M[i]!=M[i+1])z++;
    }

    printf("%d",z);
}

7-2 整数拆分 (100 分)

在这里插入图片描述

题目分析

主要是考察dfs回溯法的运用,在dfs过程中注意维护序列的不减,同时结合剩余量以及现有元素的个数进行判断。

函数int Dfs(int re,int lentgh)传入当前剩余量,以及当前元素个数。而使用

if(lentgh==m&&re!=0)return 0;
if(lentgh!=m&&re==0)return 0;

分别判断长度为m但还有剩余和长度不满m但没有剩余的情况,如果出现这两种情况就进行回溯。

代码实现如下

#include<bits/stdc++.h>
using namespace std;
int answer[100];
int n,m;
int t;
int Dfs(int re,int lentgh)
{
    if(lentgh==m&&re!=0)return 0;
    if(lentgh!=m&&re==0)return 0;
    if(lentgh==m&&re==0)
    {
        printf("%d",answer[0]);
        for(int i=1;i<lentgh;i++)
            printf(" %d",answer[i]);
        printf("\n");
        t++;
        return 0;
    }
    for(int i=answer[lentgh-1];i<=re;i++)
    {
        answer[lentgh]=i;
        Dfs(re-i,lentgh+1);
    }

}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n/m;i++)//第一位不能大于n/m取地板
    {
        answer[0]=i;
        Dfs(n-i,1);
    }
    cout<<t;
}

7-3 数字变换 (100 分)

在这里插入图片描述

题目分析

因为是求最短的路径,应该使用bfs实现,我是用的队列。

使用结构体数组q记录第Q个有效(第一次出现)的节点的信息,包括step:深度(到这一步的操作数),father:他的上一个数的下标,num:他的值,以及n:他的下标(这个有一点多余)。最后通过father和num一步步获得操作路径。

考虑到会有很多重复入队的情况,引入了visited数组记录已经入队的元素,如果该元素已经被标记那么就不对该元素进行任何处理。如果不考虑重复入队的问题,空间会爆掉为什么呢,因为队列所需的空间是呈指数级增长的,而且底数为3,大概说来,就是3的 操作数 的次方级别的空间。

另外,还有对于x>=y这种特殊情况要给予考虑(当然,>和=是两种不同的情况)

具体代码实现

#include<bits/stdc++.h>
using namespace std;
struct Node
{
    int step;
    int num;
    int father;
    int n;
};
queue<Node>stk;
int mm[100005];
int visited[200001];
Node q[100005];
int main()
{
    int a,b,Q=1;
    scanf("%d%d",&a,&b);
    if(b==a)
    {
        printf("0");
        return 0;
    }
    if(b<a)
    {
        printf("%d\n%d",a-b,a-1);
        for(int i=a-2;i>=b;i--)
            printf(" %d",i);
        return 0;
    }
    Node *x,y;
    y.step=0;
    y.num=a;
    y.n=0;
    visited[a]=0;
    stk.push(y);
    while(1)
    {
      x=&stk.front();
      stk.pop();
      if(x->num==b&&Q!=1)break;
      if(Q<100000)
      {
      q[Q].step=x->step+1;
      q[Q].num=x->num+1;
      q[Q].father=x->n;
      q[Q].n=Q;
      if(q[Q].num>=0)
      if(visited[q[Q].num]==0&&q[Q].num<=b+100)
      {
         visited[q[Q].num]=1;
         stk.push(q[Q++]);
      }
      q[Q].step=x->step+1;
      q[Q].num=x->num*2;
      q[Q].father=x->n;
      q[Q].n=Q;
      if(q[Q].num>=0)
      if(visited[q[Q].num]==0&&q[Q].num<=b+100)
      {
         visited[q[Q].num]=1;
         stk.push(q[Q++]);
      }
      q[Q].step=x->step+1;
      q[Q].num=x->num-1;
      q[Q].father=x->n;
      q[Q].n=Q;
      if(q[Q].num>=0)
      if(visited[q[Q].num]==0&&q[Q].num<=b+100)
      {
         visited[q[Q].num]=1;
         stk.push(q[Q++]);
      }
      }
    }
    int i,nn=x->step,j;
    j=x->n;
    for(i=0;i<nn;i++)
        {
            mm[i]=q[j].num;
            j=q[j].father;
        }
    printf("%d\n%d",nn,mm[nn-1]);
    for(i=nn-2;i>=0;i--)
        printf(" %d",mm[i]);
}

7-4 旅行 I (100 分)

在这里插入图片描述

题目分析

这是典型的最短路问题,本来打算先试一试时间复杂度为O(n^2)的Dijkstra算法,如果被卡时间了,就换时间复杂度为O(kE)的spfa,但这道题老师没有卡这个点。

需要注意的是,题目中有提到,在代价最小时要尽可能走更长的路,对于这一点,我引进了path数组,记录每个节点深度,在进行松弛操作时,如果权值相同,就取长度更长的路。

具体代码实现

#include<bits/stdc++.h>
using namespace std;
struct Node
{
    int name;
    int cost;
};

#define Max 10003

vector<Node> Map[Max];
int visited[Max];
long long int cost[Max];
int path[Max];

int main()
{
    int n,m,s,i,j,u;
    cin>>n>>m>>s;
    Node a;
    for(i=1;i<=n;i++)
        cost[i]=1000000;
    for(i=0;i<m;i++)
    {
        Node a;
        int x,y,t;
        cin>>x>>y>>t;
        a.name=x;
        a.cost=t;
        Map[y].push_back(a);
        a.name=y;
        Map[x].push_back(a);
    }
    int mincost;
    visited[s]=0;
    cost[s]=0;
    path[s]=0;
    for(j=1;j<=n;j++)
    {
        mincost=100000;
        for(i=1;i<=n;i++)
        {
            if(cost[i]<=mincost&&visited[i]==0)
            {
               u=i;
               mincost=cost[i];
            }
        }
        visited[u]=1;
        for(vector<Node>::iterator k = Map[u].begin();k!=Map[u].end();k++)
        {
            if(cost[k->name]>cost[u]+k->cost||cost[k->name]==cost[u]+k->cost&&path[k->name]<path[u]+1)
            {
                cost[k->name]=cost[u]+k->cost;
                path[k->name]=path[u]+1;
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        printf("%d",cost[i]);
        if(i!=n)printf(" ");
    }
    printf("\n");
    for(int i=1;i<=n;i++)
    {
        printf("%d",path[i]);
        if(i!=n)printf(" ");
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值