数据结构第四次实验报告

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

思路一:

求一个无向图的连通分量,我首先想到的就是用并查集分类,然后统计不同的类的个数即可。

#include <iostream>
using namespace std;
int n,m;
int father[100010];
int countt;
int Find(int v)
{
    if(father[v]==v) return v;
    return father[v]=Find(father[v]);
}
void Union(int x,int y)
{
    father[Find(y)]=Find(x);
}

int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++) father[i]=i;
    int x,y;
    for(int i=1;i<=m;i++)
    {
        cin>>x>>y;
        Union(x,y);
    }
    for(int i=1;i<=n;i++)
    {
        if(father[i]==i) countt++;
    }
    printf("%d",countt);

    return 0;
}

思路二:dfs/bfs搜索,遍历到的结点打标记,统计一共进行了多少次dfs/bfs.

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
using namespace std;
typedef long long ll;
vector<int>vec[50001];
int n, m, bj[50002];
int countt;
void dfs(int x)
{
    bj[x] = 1;
    for (vector<int>::iterator it=vec[x].begin();it!=vec[x].end();it++)
    {
        if (!bj[*it]) dfs(*it);
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> m;
    int x, y;
    for (int i = 1; i <= m; i++) //读入图
    {
        cin >> x >> y;
        vec[x].push_back(y);
        vec[y].push_back(x);
    }
    for (int j = 1; j <= n; j++)
    {
        if (!bj[j]) dfs(j),countt++;
    }


    printf("%d", countt);
    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

思路:dfs,每次加入一个数,保证其不低于上一个数且不超过剩余数的平均,最后达到要求的时候,最后一个数就是剩下的数。即在生成过程中保证了划分的数为递增的,且总和为N;

#include <iostream>
using namespace std;

int way[51];
int countt=0,k;


void dfs(int n,int s)
{
    if(s==k)  
    {
        way[s]=n;  //确保正好分配
        for(int  i=1;i<s;i++)
        {
            cout << way[i] << " ";
        }
        cout << way[s]<<"\n";
        countt++;
    }
    else
    {
        for(int i=way[s-1];i<=n/(k-s+1);i++)  //确保递增
        {
            way[s]=i;
            dfs(n-i,s+1);
        }
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n;
    cin >>n >> k;
    way[0]=1;  //保证第一次选取从1开始
    dfs(n,1);
    cout << countt;
    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

思路:

开始的时候我用的是stl的队列,但数组就开到200001就超限。我只好改为手写队列(也挺好写的),开了一个father数组用来记录前驱(要求是最短路径,所以出现过的就不能重复记录),同时它还能起到标志数组的作用,因为0元素是不能入队的。最后还可以通过它来确定顺序和计算次数。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <stdlib.h>
using namespace std;
int x,y;
int countt;
int father[200011];
stack<int>endd;
int que[200011],f=0,r=0;
void bfs(int xx)
{
    que[r++]=xx;
    while(f<r)
    {
        int now=que[f];
        f++;
		if(now+1<=200010&&now+1>=1&&!father[now+1])
		{ 
			que[r++]=now+1;
	        father[now+1]=now;
	        if(now+1==y)  return;
		}
        if(now*2<=200010&&now*2>=1&&!father[now*2]){
            que[r++]=now*2;
             father[now*2]=now;
            if(now*2==y)  return;
        }

        if(now-1<=200010&&now-1>=1&&!father[now-1]){
            que[r++]=now-1;
            father[now-1]=now;
            if(now-1==y)  return;
        }
    }
}
int main()
{
    scanf("%d %d",&x,&y);
    if(x==y) {cout<<0;exit(0);}
    bfs(x);
    int a=y;
    while(a!=x)
    {
        countt++;
        a=father[a];
        endd.push(a);
    }

    printf("%d\n",countt);
    endd.pop(); 
    while(!endd.empty())
    {
        int now=endd.top();
        endd.pop();
        printf("%d ",now);
    }
    printf("%d",y);
}

 

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 <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
using namespace std;
typedef long long ll;
struct node
{
	int a;//bianhao
	ll b;//cost
};
bool operator<(node aa, node bb)
{
	return aa.b > bb.b;
}
const int INF = 1e9 + 5;
int n, m, u;
vector<pair<int, int> >vec1[10001];
priority_queue<node>pq1, pq2;
ll dist[10001];
int way[10001];
int bj1[10001];
void dijkstra()
{
	bj1[u] = 1;
	way[u]= 0;
    for (int k = 1; k < n; k++)
    {
        for (vector<pair<int, int> >::iterator it = vec1[u].begin(); it != vec1[u].end(); it++)
        {
            int y = it->first;
            ll w = it->second;
            if (!bj1[y] && dist[u] + w < dist[y])
            {
                dist[y] = dist[u] + w;
                way[y]=way[u]+1;
                node sja;
                sja.a = y;
                sja.b = dist[y];
                pq1.push(sja);
            }
            if (!bj1[y] && dist[u] + w == dist[y])
            {
                dist[y] = dist[u] + w;
                way[y]=max(way[u]+1,way[y]);
                node sja;
                sja.a = y;
                sja.b = dist[y];
                pq1.push(sja);
            }
            
        }
        node minn;
        do
        {
            minn = pq1.top();
            pq1.pop();
        } while (bj1[minn.a]);
        u = minn.a;
        bj1[u] = 1;
    }
}
int main()
{
	cin.tie(0);
	cin >> n >> m >> u;
	int x, y, t;
	for (int i = 1; i <= m; i++)
	{
		cin >> x >> y >> t;
		vec1[x].push_back(make_pair(y, t));
		vec1[y].push_back(make_pair(x, t));
	}
	for (int i = 1; i <= n; i++)
	{
		dist[i] = INF;
	}
	dist[u] = 0;
	dijkstra();
	for(int i=1;i<n;i++)
    {
        printf("%lld ",dist[i]);
    }
    printf("%lld\n",dist[n]);

    for(int i=1;i<n;i++)
    {
        printf("%d ",way[i]);
    }
    printf("%d\n",way[n]);
}

 

 

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值