四种主要算法经典题解

NEFU大二下学期算法分析与设计课程设计内容,四大主要算法个选其一。


一、分治策略

1、输油管道问题
【题目描述】
某石油公司计划建造一条由西向东的主输油管道,该管道要穿过一个有n口油井的油田。从每口油田都要有一条输油管道沿最短路径(或南或北)与主管道相连。如果给定n口油井的位置,即它们的x坐标(东西向)和y坐标(南北向),应如何确定主管道的最优位置,即使各油井到主管道之间的输油管长度总和最小的位置?
【输入】
第一行是一个整数n,表示油井数量(1-1000之间),接下来n行是油井的位置,每行两个整数x和y。
【输出】
各油井到主管道之间的输油管道最小长度总和。
【输入样例】
5
1 2
2 2
1 3
3 -2
3 3
【输出样例】
6

解题思路

油管是自西向东的主管道,油井是分布在主管道周围,但计算油井到主管道距离时与横坐标无关,只需要油井纵坐标减去主管道所在的纵坐标的绝对值即可。
求油管的纵坐标就找中位数,找中位数用快速排序算法。
代码如下(示例):

#include <iostream>
#include <math.h>
using namespace std;
        /*油管问题
        【输入样例】第一行是一个整数n,表示油井数量(1-1000之间)
        接下来n行是油井的位置,每行两个整数x和y。
5
1 2
2 2
1 3
3 -2
3 3
        【输出样例】各油井到主管道之间的输油管道最小长度总和。
        6
        思路:油管是自西向东的主管道,油井是分布在主管道周围,
        但计算油井到主管道距离时与横坐标无关,只需要油井纵坐标减去主管道所在的纵坐标的绝对值即可。
        求油管的纵坐标就找中位数,找中位数用快速排序算法。
        */
void quick_sort(int q[], int l, int r)
{
    
    
    if (l >= r) return;

    int i = l - 1, j = r + 1, x = q[(l + r)/2];
    while (i < j)
    {
    
    
        do i ++ ; while (q[i] < x);
        do j -- ; while (q[j] > x);
        if (i < j) swap(q[i], q[j]);
    }
    quick_sort(q, l, j), quick_sort(q, j + 1, r);
}
    void min_road_sum(int a[],int n)
    {
    
    
     int mid=a[n>>1];
    int sum=0;
    for(int i=0;i<n;i++)
        sum+=fabs(mid-a[i]);
    printf("油管主干道长度为%d",sum);
    }
int main()
{
    
    
    int a[100];
    int n;
    printf("请输入n个油井的数目\n并依次输入位置\n");
    scanf("%d",&n);
    for(int i=0,x;i<n;i++)
        scanf("%d %d",&x,a+i);
    quick_sort(a,0,n-1);
    min_road_sum(a,n);
    return 0;
}

二、动态规划

1.台阶路径

给定一个矩阵m,从左上角开始每次只能向右走或者向下走,最后达到右下角的位置,路径中所有数字累加起来就是路径和,返回所有路径的最小路径和,如果给定的m如下,那么路径1,3,1,0,6,1,0就是最小路径和,返回12.
1 3 5 9
8 1 3 4
5 0 6 1
8 8 4 0

代码如下(示例):

#include <iostream>
#include<algorithm>
/*
4
1 3 5 9
8 1 3 4
5 0 6 1
8 8 4 0
*/
void put(int d[][100],int n)
{
    
    
    for(int i=0;i<n;i++)
    {
    
    for(int j=0;j<n;j++)
    printf("%d ",d[i][j]);
    printf("\n");
    }

}

using namespace std;
    int main()
    {
    
    
        int a[100][100],n;
        int d[100][100],c[100];
        scanf("%d",&n);
                for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
            scanf("%d",&a[i][j]);
        d[0][0]=a[0][0];
        for(int i=1;i<n;i++)
            {
    
    d[0][i]=a[0][i]+d[0][i-1];d[i][0]=a[i][0]+d[i-1][0];}
        for(int i=1;i<n;i++)
            for(int j=1;j<n;j++)
            d[i][j]=a[i][j]+(d[i-1][j]>d[i][j-1]?d[i][j-1]:d[i-1][j]);
      // printf("矩阵如下:\n");
       // put(a,n);
        printf("  最短路径长度是%d\n  路径是:\n开始:%d->",d[3][3],a[0][0]);
        for(int i=0,j=0;i<n-1;i++)
        {
    
    
            if(d[i][j]==d[n-1][n-1])break;
            if(d[i][j+1]<d[i+1][j])
            {
    
    
                printf("%d->",a[i][j+1]);j++;i--;
            }
            else{
    
    
                printf("%d->",a[i+1][j]);
            }

        }
        printf("%d->结束",a[n-1][n-1]);
        return 0;
    }

2.动态规划方程

动态规划方程是:d[i][j]=a[i][j]+min(d[i-1][j],d[i][j-1]);

三、贪心策略

站台安排

一个火车站有一个所有火车到达和离开的时间表,需要找出最小站台数,使得按照此时间表调度时,可以容纳所有的火车。已知火车时刻表如下所示,试编程实现,需要的最少站台有几个。

火车 到达时间 离开时间 火车 到达时间 离开时间
车次A 0900 0930 车次C 1030 1100
车次B 0915 1300 车次D 1045 1145

代码如下:

#include <iostream>
/*
测试数据
第一组
4
0900 0930
0915 1300
1030 1100
1045 1145

第二组
6
8 11
1 4
5 7
3 5
6 10
0 6
*/
using namespace std;

void sort(int a[][100],int n)
{
    
    
    for(int i=0;i<n-1;i++)
      for(int j=0;j<n-i-1;j++)
    {
    
    
        if(a[j][1]>a[j+1][1])
        {
    
    
            int t=a[j][1],m=a[j][0];
            a[j][1]=a[j+1][1];a[j][0]=a[j+1][0];
            a[j+1][1]=t;a[j+1][0]=m;
        }
    }
}

    void GreedySelector(int n,int s[][100],int a[],int x,int k)
    {
    
    
        a[k]=x;
        int j=k;
        for(int i=k+1;i<n;i++)
        {
    
    
            if(s[i][0]>s[j][1])
            {
    
    
                a[i]=x;j=i;
            }

        }
    }

int main()
{
    
    
    int s[100][100];
    int n,i,a[100];
    //a数组是标记数组,标记第i+1列车是否被安排站台
    printf("第一行请输入列车到达总数n\n第二行起依次输入每列列车的到达时间和离站时间\n");
    scanf("%d",&n);
    for(i=0;i<n;i++)
        {
    
    scanf("%d %d",&s[i][0],&s[i][1]);
            a[i]=0;
        }
    sort(s,n);//按照离站时间排序
        int x=1;//为火车安排第x个站台
      for(i=0;i<n;i++)
           {
    
    
               if(a[i]!=0)continue;
               GreedySelector(n,s,a,x,i);
               x++;
           }
      int m=-1;
      for(i=0;i<n;i++)
        if(a[i]>m)m=a[i];
    printf("    需要安排%d个站台\n",m);
    for(i=0;i<n;i++)
        printf("第%d列火车安排在第%d个站台\n",i+1,a[i]);
    return 0;
}

四、回溯

素数环

(1)问题描述:输入正整数n,把整数1,2,3,4……n组成一个环,使得相邻的两个整数之和均为素数。
(2)样例
输入:
6
输出:
1 4 3 2 5 6
1 6 5 2 3 4
(3)n=4时的搜索解空间树。
在这里插入图片描述

代码如下:

#include <iostream>
#include <algorithm>
using namespace std;

    int a[100],n,is_use[100];
    int is_prime(int x)
    {
    
    
        for(int i=2;i<x;i++)
            if(x%i==0)return 0;
        return true;
    }


    void dfs(int t)
    {
    
    
        if(t==n&&is_prime(a[t-1]+a[0]))
            //判断最后一个数与首位数的和是否为素数
        {
    
    
                cout<< "  素数环是" << endl;
            for(int i=0;i<n;i++)
                cout<< a[i] << " ";
            printf("\n\n");
        }
        else
            {
    
    
            for(int i=2;i<=n;i++)
            {
    
    
                if(is_prime(i+a[t-1])&&!is_use[i])
                    //判断选入的数是否与前一个数之和为素数,并且重复选入
                {
    
    
                    a[t]=i;
                    is_use[i]=1;
                    dfs(t+1);
                    a[t]=0;
                    is_use[i]=0;
                }
            }


            }

    }
    int main()
    {
    
    
        cout<<"  请输入素数环的范围n:"<<endl;
        cin>>n;
        for(int i=2;i<n;i++)
            is_use[i]=0;
        a[0]=1;//首位为1,因为是环,哪个数在第一个都一样,默认首位是1
        dfs(1);
        return 0;
    }

总结

算法课程自己学的不好,这学期补课忙到爆炸,时间和精力花在算法上极少,很是难过。幸亏我们学校的课程设计不难,都能讨论找资料完成,也就浑水摸鱼混过去了。一定要好好学习算法!

おすすめ

転載: blog.csdn.net/weixin_51121822/article/details/118192224