《算法分析与设计》练习13

目录

A 迷路的牛牛

B 工作单位

C 隔离14天

D 最小生成树(kruskal)

E 搭建电路

F 单源最短路径问题


A 迷路的牛牛

题目描述

牛牛去犇犇老师家补课,出门的时候面向北方,但是现在他迷路了。虽然他手里有一张地图,但是他需要知道自己面向哪个方向,请你帮帮他。

输入

每个输入包含一个测试用例。

每个测试用例的第一行包含一个正整数,表示转方向的次数N(N<=1000)。

接下来的一行包含一个长度为N的字符串,由L和R组成,L表示向左转,R表示向右转。

输出

输出牛牛最后面向的方向,N表示北,S表示南,E表示东,W表示西。

样例输入 Copy

3

LRR

样例输出 Copy

E

分析:我直接说思路啦,首先记录L的个数和 R 的个数。

然后草稿纸上分析,对4取余。(四个方向  )W,S,E,N。

我们让L的个数减去 R 的个数,如果大于 0,我们只要全部向左转。

否则,我们就全部向右转。

if 是左转, 余数 为1时,  输出 W

                 余数 为 2 时, 输出S

                 余数 为 3 时, 输出 E

                余数 为 0 时, 输出 N

if 是右转 , 余数 为1时,  输出 E

                 余数 为 2 时, 输出S

                 余数 为 3 时, 输出 W

                余数 为 0 时, 输出 N

就是这样敲代码就好了。

代码实现:c语言


 

#include <stdio.h>

#include <stdlib.h>



int main (){

char a[2000];

int n;

while(~scanf("%d",&n)){

    scanf("%s",a);

    solve(a,n);



}





return 0;

}







void solve (char a[],int n){

    int lc=0;

    int rc=0;

for(int i=0;i<n;i++)

    if(a[i]=='L')

    lc++;

else rc++;

int count= lc-rc;



    if(count>0) {

        if(count%4==0) printf("N\n");

        if(count%4==1) printf("W\n");

        if(count%4==2) printf("S\n");

        if(count%4==3) printf("E\n");

    }

    else {

        count =0-count;

        if(count%4==0) printf("N\n");

        if(count%4==1) printf("E\n");

        if(count%4==2) printf("S\n");

        if(count%4==3) printf("W\n");

    }

}



B 工作单位

样例输入 Copy

10 4

2 3

4 5

4 8

5 8

样例输出 Copy

7

分析:先假设一个人在一个单位上班,只要两个人在同一家单位上班,我们就 减一

以样例输入为例,2 和 3 在一个单位,10-1;

                           4 和 5 在一个单位    10-1-1;

                           4 和 8 在一个单位    10-1-1-1;

                           最后的 5 和 8 在一个单位,可以根据 前面两个推出来,所以不需要再 -1了。

然后为什么可以用并交集来解呢?

其实它刚好符合这种模型。 刚开始  2 和 3 的根节点 都是  它们本身 2,3  刚好也就符合  我们假设  的 2 和  3 在不同单位上班。

既然不在一个 单位上班,现在我们有需要在一个单位上班 ,于是  我们就  把 2 和  3 合并 起来,对应代码的话,2的根节点就是 3。

依此类推,4和 5 ,合并之后 , 4的根节点就是 5

                  4和8,合并之后,8的根节点也是 5

                  5和8 ,合并之后没反应,因为,他们的根节点都是 5.

题目思路就是这样,具体不懂的这个  并交集等上课内容,还是多看看老师的ppt。

代码实现:c语言

#include <stdio.h>

#include <stdlib.h>



int parents[200];

int rank[200];

int n,m;

int count;

int main (){

while(~scanf("%d %d",&n,&m)){

    int x;

    int y;

    count=n;

    init();

    for(int i=0;i<m;i++)

    {

        scanf("%d %d",&x,&y);

        union_set(x,y);

    }

    printf("%d",count);

    printf("\n");

}







return 0;

}





void init(){

for(int i=1;i<=n;i++)

{

    parents[i]=i;

    rank[i]=0;

}

}



int findparents(int x){

if(parents[x]!=x)

   parents[x]=findparents(parents[x]);

   return parents[x];



}



void union_set(int x,int y){

x=findparents(x);

y=findparents(y);

if(x==y) return;

count--;

if(rank[x]>rank[y]){

    parents[y]=x;

}

else{

    parents[x]=y;

    if(rank[x]==rank[y])

        rank[y]++;

}



}

C 隔离14天

样例输入 Copy

100 4

2 1 2

5 10 13 11 12 14

2 0 1

2 99 2

样例输出 Copy

4

分析:此题要用到并交集,我就不讲那个上课内容啦,很多很多看老师ppt理解最好。

我就讲讲我的解题思路。

先 写初始化函数, init(),让 所有人的 根节点是他本身,所有的 rank=0;

再 写找根节点函数 ,一定要是根节点。findparents();

再 写结合函数,两个根节点不一样的树 结合在一起。

那怎么找出 是 和 0在一起的人呢?

很简单,我们只要找出 和 0一样的根节点即可,为什么呢:

因为 有人 和 0有关联, 那么,这个人要么是 0的 父节点,要么就是 0的 子节点,很显然 ,他们的根节点是一样的。

那如果出现这种间接的情况呢? 比如:0  1 

                                                          0  2

                                                           9  2。

我们如何 判定呢 9 肯定也要 隔离14 天呢?

很简单,还是上面说的,只要 9的根节点和  0 的根节点相同就是了。

比如 :(假如我都以后面的数 为准,不管rank了 )

               0和1 结合,  根节点就是 1;

           再 0和 2结合, 0之前的根节点是 1,2的根节点是它本身,所以结合之后  ,0,1,2的根节点全是 2;

           再 9 和 2 结合 , 9之前的根节点是它本身,2的是2,所以 结合后,0,1,2,9的 根节点都是 2。

很明显,无论什么情况都能解了。

细节的地方,在于那个 读数的时候怎么读,很简单,我们就用一个循环即可,因为前面已经给出 可以循环的次数了。

然后分别把后面的数值 与 第一次循环 读入的数相结合即可。是一样的。

代码实现:c语言


 

#include <stdio.h>

#include <stdlib.h>

#define ll long long



int parents[200];

int rank[200];



int main (){

int n;

int m;

while(~scanf("%d %d",&n,&m)){

      

    init(n);

    for(int i=0;i<m;i++)

    {

        int number;

        int x;

        int temp;

        scanf("%d %d",&number,&x);

        for(int i=1;i<number;i++)

        {

            scanf("%d",&temp);

            union_set(x,temp);

        }



    }

    int count=0;

    for(int i=0;i<n;i++)

        if(parents[i]==parents[0])

        count++;

    printf("%d",count);

    printf("\n");

}





return 0;

}



void init (int n){

for(int i=0;i<n;i++){

    parents[i]=i;

    rank[i]=0;

}

}



int findparents(int x){

if(parents[x]!=x)

    parents[x]=findparents(parents[x]);

return parents[x];



}



void union_set(int x,int y){

x=findparents(x);

y=findparents(y);

if(x==y) return;

else{

    if(rank[x]>rank[y])

        parents[y]=x;

    else{

        parents[x]=y;

        if(rank[x]==rank[y])

            rank[y]++;

    }

}



}



D 最小生成树(kruskal)

题目描述

编程实现Kruskal算法,求图的最小生成树(MST)的权重。

输入

每组数据分为两个部分,第一部分为图的点数n,和边数m, 

第二部分为m行,每一行输入三个数字,前两个为两个顶点的编号,第三个为边权重。 

输出

最小生成树的权重。

样例输入 Copy

3 3

0 1 10

0 2 15

1 2 50

样例输出 Copy

25

分析:这个题我就不多说了,上课多听老师讲,不要去作过多的笔记,网上都会有代码的,比如关注我

嘿嘿嘿。要用到 并交集。为什么要用到并交集呢?

因为它可以帮助我们检索是否已经用到 某两点了。

说到这,可能还有人不是很懂 ,在于kruskal,

kruskal就是把从小到大选边,只要这两条边够小,且其中关联的点,没有构成环路,即可。

n个点,我们也就只要选n-1条边即可。

所以综上我们什么要用到并交集, 因为结合后,他们的根节点总是相等,而我们可以

以此判断是否 构成回路。

代码实现:c语言

#include <stdio.h>

#include <stdlib.h>



struct shu{

int d1;

int d2;

int bian;



};

struct shu s[200];



int cmp(const void *a,const *b){

struct shu *c=(struct shu *)a;

struct shu *d=(struct shu *)b;

return c->bian-d->bian;



}



int parents[200];

int rank[200];

int sum =0;

int main (){

int n;

int m;



while(~scanf("%d %d",&n,&m)){

        init(n);

    for(int i=0;i<m;i++)

        scanf("%d %d %d",&s[i].d1,&s[i].d2,&s[i].bian);

    qsort(s,m,sizeof(s[0]),cmp);

    for(int i=0;i<m;i++)

        union_set(s[i].d1,s[i].d2,s[i].bian);

    printf("%d",sum);

    printf("\n");

    sum=0;

}





return 0;

}









void init (int n){

for(int i=0;i<n;i++)

{

    parents[i]=i;

    rank[i]=0;

}



}



int findparents(int x){

if(parents[x]!=x)

    parents[x]=findparents(parents[x]);

return parents[x];

}





void union_set(int x,int y,int bian){

x=findparents(x);

y=findparents(y);

if(x==y) return;

else{

    sum+=bian;

    if(rank[x]>rank[y])

        parents[y]=x;

    else{

        parents[x]=y;

        if(rank[x]==rank[y])

            rank[y]++;

    }

}

}

E 搭建电路

题目描述

明明迷上了一个搭建电路的游戏。

在游戏中,每次在两个电子元件之间增加一条有效电路(两个元件之间先前没有电路相连)都将获得相应的积分奖励。

已知电子元件数量n和部分电子元件之间的奖励积分值。如何构建一个有效电路将所有元件全部连接起来,并且可以得到最多的积分奖励。

输入

每组输入数据包含m+1行。

第1行输入两个正整数n和m,其中n表示电子元件数量(n<=100),m表示提供了m对电子元件之间的奖励积分值(m<=1000)。两个正整数之间用空格隔开。

第2行到第m+1行对应m对电子元件及其对应的奖励积分值,每一行包含三个正整数,第1个和第2个整数表示电子元件编号(从1开始),第3个整数表示两个元件之间搭建电路的奖励积分num(num<1e9)。整数之间用空格隔开。

输出

每组输出占1行,输出一个正整数,即最多可以得到的积分奖励值。如果没有办法把所有元件全部连接起来,则输出“No solution.”。

样例输入 Copy

3 3

1 2 10

1 3 20

2 3 30

样例输出 Copy

50

分析:这题和上题一样的东西,就是求个最大值,把排序换一下就可以了。

啊这,不知道为什么,这道题,我用c语言做不出来。

总是运行错误。又是上次的那种错误,看别人的,本质上一摸一样,但是看了近3个小时了,还是不行。唉,copy了别人的代码

代码实现:c++


#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

struct shu
{
    int x;
    int y;
    int bian;
};

bool cmp(shu a,shu b){
return a.bian>b.bian;

}

int parents[2000];
int rankk[2000];

shu a[2000];

ll sum;

int find_set(int x){
if(x!=parents[x])

parents[x]=find_set(parents[x]);
return parents[x];

}



int union_set(int x,int y,int bian){
    x=find_set(x);
y=find_set(y);
if(x==y)
return 0;
sum+=bian;
if(rankk[x]>rankk[y]){
parents[y]=x;
}

else{
parents[x]=y;
if(rankk[x]==rankk[y]){
rankk[y]++;
}
}
return 1;
}

int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n,m;
cin>>n>>m;
for (int i=0;i<=n;i++)
{
parents[i]=i;
rankk[i]=0;
}
for(int i=0;i<m;i++){
cin>>a[i].x>>a[i].y>>a[i].bian;
}
sum=0;
sort(a,a+m,cmp);
int flag = 0;
for (int i=0; i<m;i++)
{
int res=union_set(a[i].x,a[i].y,a[i].bian);
if(res){
flag++;
}
}
if(flag==n-1)
cout<<sum<<endl;
else
cout<<"No solution."<<endl;
return 0;

}

F 单源最短路径问题

题目描述

编程实现Dijkstra算法,求一个有向加权图中,从源点出发到其他各个顶点的最短路径。

输入

第1行第1个值表示顶点个数,第2个值表示边个数;第2行开始为边(两个顶点,边的起点和终点)及权重。

输出

顶点0到每一个顶点的最短路径长度。

样例输入 Copy

5 7

0 1 10

0 3 30

0 4 100

1 2 50

2 4 10

3 2 20

3 4 60

样例输出 Copy

0 10 50 30 60

分析:题目说了啊,要用Dijkstra算法,求0点到 其他点的最短距离

额,直接说思路。

先初始化,把图上的所有的点与点之间的距离都设置为 无穷大(INF);

然后  再一个一个读入  img数组中。

然后就可以启动我们的  djikstra 方法;

这个方法嘞,先还是初始化,所有的点都没有被用到。used[i]=0;

                                           另外用一个数组来表示  0点到 所有点的距离  dis[i]=img[0][i];

然后进行 n-1次 循环操作,每次都要找出距离最短的点,并进行更新操作。

其他的内容就是 djikstra的了,上课认真听讲。

代码实现:c语言


 

#include <stdio.h>

#include <stdlib.h>



int INF=10000;

int img[200][200];



int main (){

int n;

int m;

while(~scanf("%d %d",&n,&m)){

    init(n);

    int x;

    int y;

    int bian;



    for(int i=0;i<m;i++)

    {

         scanf("%d %d %d",&x,&y,&bian);

             img[x][y]=bian;

    }

   dijkstra(n);





}







return 0;

}



void init (int n){

for(int i=0;i<n;i++)

    for(int j=0;j<n;j++)

    img[i][j]=INF;

     img[0][0]=0;

}



void dijkstra(int n){

    int dis[200];

    int used[200];

    for(int i=0;i<n;i++)

       {

           dis[i]=img[0][i];

           used[i]=0;

       }





for(int i=1;i<n;i++){

    int min=INF;

   int k;

    for(int j=0;j<n;j++)

    {

        if((!used[j])&&dis[j]<min){

            min=dis[j];

            k=j;

        }

    }

    used[k]=1;

    for(int j=0;j<n;j++)

    {

        if((!used[j])&&(dis[k]+img[k][j]<dis[j])){

            dis[j]=dis[k]+img[k][j];

        }

    }



}

for(int i=0;i<n;i++)

    printf("%d ",dis[i]);

    printf("\n");



}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值