算法考试题

分治法课堂案例 

第1关:二分搜索技术

任务描述

本关任务:给定一组有序整数,用二分查找技术查找X是否在序列中,在则输出Yes,不在则输出No。 输入格式:三行,第一行一个整数n,第二行n个正整数,且有序;第三行是要查找的X 输出格式:X在序列中输出下标(0开始),不在输出-1

编程要求

根据提示,在右侧编辑器补充代码。

测试说明

平台会对你编写的代码进行测试:

测试输入: 10 11 21 22 32 41 51 52 62 71 82 22 预期输出: 2


开始你的任务吧,祝你成功!

答案:

#include<iostream>
using namespace std; 
#include<stdio.h>
const int N = 10000; 
int BSearch(int a[], int l, int r,int x)
{
/***********Begin***************/
int m=(l+r)/2;
while(l<=r)
{
  if(a[m]>x)
  {
    r=r-1;
    m=(l+r)/2;
  }
  else if(a[m]<x)
  {
    l=l+1;
    m=(l+r)/2;
  }
  else
  {
  
    break;
  }

}
if(l<=r)
return m;
else
return -1;



/***********End***************/
}
int main()
{
  int n,x,a[N];
  cin>>n;  

  for(int i = 0; i < n; i++)
     cin>>a[i];
  cin>>x;  
  cout<<BSearch(a,0,n-1,x);
  return 0;


}

第2关:棋盘覆盖问题

任务描述

在一个

解释

2k×2k个方格组成的棋盘中,恰有一个方格与其它方格不同,称该方格为一特殊方格,且称该棋盘为一特殊棋盘。在棋盘覆盖问题中,要用图示的4种不同形态的L型骨牌覆盖给定的特殊棋盘上除特殊方格以外的所有方格,且任何2个L型骨牌不得重叠覆盖。

,

例如:4×4的棋盘的一种覆盖方法为:

,

编写函数,输出给定k的一种棋盘覆盖。

编程要求

根据提示,在右侧编辑器补充代码。

输入格式

输入为三个正整数: size dr dc 其中(size=2k,0≤k≤5),(dr,dc)是特殊方格的位置,且0≤dr,dc≤size−1

输出格式

输出棋盘覆盖方案,特殊方格处输出-1,其他位置处同一编号的L形骨牌用同一个数字表示,数字占宽4格,右对齐。

输入输出示例

测试输入:8 3 2 预期输出:

 
  1. 2 2 3 3 7 7 8 8
  2. 2 1 1 3 7 6 6 8
  3. 4 1 5 5 9 9 6 10
  4. 4 4 -1 5 0 9 10 10
  5. 12 12 13 0 0 17 18 18
  6. 12 11 13 13 17 17 16 18
  7. 14 11 11 15 19 16 16 20
  8. 14 14 15 15 19 19 20 20

开始你的任务吧,祝你成功!

答案:

#include<iostream>
#include<iomanip>
# define SIZE 32
using namespace std;

int tile = 0;
int Board[SIZE][SIZE];
//棋盘以(tr,tc)为左上角,以size为边长,特殊方格位置在(dr,dc),ChessBoard函数用分治法
//完成它四个子棋盘覆盖
void ChessBoard(int tr, int tc, int dr, int dc, int size)
{
/************Begin**************/
if(size==1) return ;
int t=tile++;
int s=size/2;
if(dr<tr+s&&dc<tc+s)
    ChessBoard(tr,tc,dr,dc,s);
else{
  Board[tr+s-1][tc+s-1]=t;
  ChessBoard(tr,tc,tr+s-1,tc+s-1,s);
}
if(dr<tr+s&&dc>=tc+s)
    ChessBoard(tr,tc+s,dr,dc,s);
else{
  Board[tr+s-1][tc+s]=t;
  ChessBoard(tr,tc+s,tr+s-1,tc+s,s);
}
if(dr>=tr+s&&dc<tc+s)
    ChessBoard(tr+s,tc,dr,dc,s);
else{
  Board[tr+s][tc+s-1]=t;
  ChessBoard(tr+s,tc,tr+s,tc+s-1,s);
}
if(dr>=tr+s&&dc>=tc+s)
    ChessBoard(tr+s,tc+s,dr,dc,s);
else{
  Board[tr+s][tc+s]=t;
  ChessBoard(tr+s,tc+s,tr+s,tc+s,s);
}
/************End**************/

}
int main()
{
    /************Begin**************/
    int size,dr,dc;
    cin>>size>>dr>>dc;
    Board[dr][dc]=-1;
    ChessBoard(0,0,dr,dc,size);
    /************End**************/
    for (int i = 0; i < size; i++) {
		for (int j = 0; j < size; j++)
			cout << setw(4) << Board[i][j];
		cout << endl;
    }
return 0;
}

第3关:合并排序 

任务描述

本关任务:用合并排序方法对数组a元素排序。 输入:两行,第一行是正整数n,第二行是n个正整数 输出:排好序的一行正整数

编程要求

根据提示,在右侧编辑器补充代码。

测试说明

平台会对你编写的代码进行测试:

测试输入: 8 4 5 3 2 9 7 1 8 预期输出: 1 2 3 4 5 7 8 9


开始你的任务吧,祝你成功!

答案:

#include<iostream> 
using namespace std;  
const int N = 1000;
/*******Begin***********/
void Merge(int a[],int b[],int l,int i,int r)
{
    int j=l,k=i+1,m=l;
 while(j<=i&&k<=r)
 {
     if(a[j]>a[k])
     {
         b[m]=a[k];
         k++;
         m++;
     }
     else
     {
         b[m]=a[j];
         j++;
         m++;
     }
 }
 while(j<=i)
 {
     b[m]=a[j];
     m++;
     j++;
 }
 while(k<=r)
 {
     b[m]=a[k];
     m++;
     k++;
 }
 for(int u=l;u<=r;u++)
 a[u]=b[u];
}
void MergeSort(int a[],int b[],int l,int r )
{
    if(l<r)
    {
        int i=(l+r)/2;
        MergeSort(a,b,l,i);
        MergeSort(a,b,i+1,r);
        Merge(a,b,l,i,r);
       
    }
}






/*******End***********/


int main( )
{
    int n,a[N],b[N]; 
    cin>>n; 
    for(int i = 0 ; i < n; i ++)cin>>a[i]; 
    MergeSort(a,b,0,n-1);
    for(int i = 0 ; i < n - 1; i ++)cout<<a[i]<<" "; 
    cout<<a[n-1];
    return 0;

}

第4关:快速排序 

任务描述

本关任务:用快速排序方法对数组a元素排序。 输入:两行,第一行是正整数n,第二行是n个正整数 输出:排好序的一行正整数

编程要求

根据提示,在右侧编辑器补充代码。

测试说明

平台会对你编写的代码进行测试:

测试输入: 8 4 5 3 2 9 7 1 8 预期输出: 1 2 3 4 5 7 8 9


开始你的任务吧,祝你成功!


开始你的任务吧,祝你成功!

答案:

#include<iostream> 
using namespace std;    
const int N = 1000;  
  
// 快速排序的划分函数  
int Partition(int a[], int left, int right) {  
    int pivot = a[right]; // 选择最右边的元素作为基准  
    int i = left - 1; // 指向小于基准的元素的最后一个位置  
    for (int j = left; j < right; j++) {  
        if (a[j] <= pivot) {  
            i++;  
            swap(a[i], a[j]); // 交换元素,使得小于基准的元素在左边  
        }  
    }  
    swap(a[i + 1], a[right]); // 将基准元素放到最终位置  
    return i + 1;  
}  
  
// 快速排序的递归函数  
void QuickSort(int a[], int left, int right) {  
    if (left < right) {  
        int pivotIndex = Partition(a, left, right);  
        QuickSort(a, left, pivotIndex - 1); // 递归处理左半部分  
        QuickSort(a, pivotIndex + 1, right); // 递归处理右半部分  
    }  
}  
  
/**************Begin*********/  
  
// 在这里不需要额外添加代码,因为上面的QuickSort函数已经足够用于排序  
  
/**************End*********/  
  
int main() {  
    int n, a[N];  
    cin >> n;  
    for (int i = 0; i < n; i++) cin >> a[i];  
    QuickSort(a, 0, n - 1); // 调用快速排序函数  
    for (int i = 0; i < n - 1; i++) cout << a[i] << " ";  
    cout << a[n - 1];  
    return 0;  
}

第5关:线性时间选择问题 

任务描述

给定线性无序数组n个元素和一个正整数k,1≤k≤n,要求在线性时间找到这n个元素的第k小。

相关知识
  1. 排序求第K个元素。由于排序算法的时间复杂度都在O(nlogn),因此不满足线性时间要求。
  2. 借用快速排序中的划分Partition思想,选一个基准元素,将比基准元素小的放到左侧,比基准元素大的放到右侧,如果基准元素的位置是j,则比较k与j的大小:
  • k==j 则基准元素刚好是第k小元素,返回
  • k<j 则第k小在左侧,对左侧递归找第k小
  • k>j 则第k小在右侧,对右侧递归找第j-k小
  1. 该方法效率取决于每次选择的基准元素,如果基准元素能将数组每次分成ϵn1−ϵn0<ϵ<1)两部分,则能在线性时间完成找第k小任务;如果每次选择基准元素是最大值或最小值,则退化成最坏情况,时间复杂度为O(n2)
  2. 问题的关键变成如何在线性时间找一个基准元素,能将数组划分成ϵn1−ϵn0<ϵ<1)两部分.思想如下:
  • 将所有元素每5个一组,分成n/5组,将每组的中位数找到
  • 以所有中位数的中位数做为基准元素
编程要求

完成一个冒泡排序函数:

 
  1. void BubbleSort(Type a[],int p,int r)

完成根据基准元素x进行划分的函数:

 
  1. int Partition(int a[],int p,int r,Type x)

完成线性时间选择数组a[p]~a[r]的第k小函数:

 
  1. int Select(int a[],int p,int r,int k)
  • 数组元素个数小于等于75时,直接用冒泡排序,返回第k小
  • 数组元素个数大于等于75时,调用Select函数,返回第k小
测试说明
  • 函数中数组的元素是整型,返回值也是整型。

  • 输入的数组元素是整型,且不重复。 主函数如下:

     
      
    1. #include<iostream>
    2. using namespace std;
    3. int main()
    4. {
    5. int a[1000]={0},n,k;
    6. cin>>n;
    7. for(int i = 0; i < n; i ++)
    8. cin>>a[i];
    9. cin>>k;
    10. cout<<Select(a,0,n-1,k)<<endl;
    11. return 0;
    12. }
  • 直接完成要求函数即可,输入输出由隐藏主函数完成。

开始你的任务吧,祝你成功!

答案:

void Swap(int &a,int &b)
{//交换函数
	 /*********begin************/
    int c;
	c = a;
	a = b;
	b = c;

    /*********end************/
}


int Partition(int a[],int p,int r,int x)
{//以x为基准划分函数
	 /*********begin************/


    /*********end************/
}

void BubbleSort(int a[],int p,int r)
{//冒泡排序
    
    /*********begin************/
    int i,j,temp;
    for(i=0;i<r;i++){
        for(j=0;j<r-i;j++){
            if(a[j]>a[j+1]){
                temp=a[j];
                a[j]=a[j+1];
                a[j+1]=temp;
            }
        }
    }
    /*********end************/
	
}
int Select(int a[],int p,int r,int k)
{
    /*********begin************/
    if(k<=75){
        BubbleSort(a,p,r);
        return a[p+k-1];
    }

    



    /*********end************/
}

第6关:一维空间最短距离 

任务描述

本关任务:S中的n个点为x轴上的n个实数。最接近点对即为这n个实数中相差最小的两个实数。给定n个实数,用分治法求最接近点对的距离。 例如: 11个点: 1.1 4.5 3.2 2.7 7.8 9.1 5.5 1.9 7.4 9.9 2.2 最接近点对距离是:0.3

编程要求

根据提示,在右侧编辑器补充代码。

测试说明

输入格式:两行,第一行正整数N,第二行n个实数 输出格式:这n个实数的最接近点对距离

测试输入: 11 1.1 4.5 3.2 2.7 7.8 9.1 5.5 1.9 7.4 9.9 2.2 预期输出: 0.3


开始你的任务吧,祝你成功!

答案:

#include <iostream>  
#include <vector>  
#include <algorithm>  
#include <cmath>  
using namespace std;  
  
const double MAX = 9999.99;  
const int N = 1000;  
  
// 用于存储点的结构体  
struct Point {  
    double x;  
    int index; // 存储原始数组中的索引,以便找到对应的点  
};  
  
// 辅助函数:比较两个点的x坐标  
bool cmpX(const Point& a, const Point& b) {  
    return a.x < b.x;  
}  
  
// 辅助函数:比较两个点的y坐标  
bool cmpY(const Point& a, const Point& b) {  
    return a.x - a.index < b.x - b.index;  
}  
  
// 计算两点之间的距离  
double distance(const Point& a, const Point& b) {  
    return fabs(a.x - b.x);  
}  
  
// 辅助函数:寻找条带内的最小距离  
double stripClosest(const vector<Point>& strip, int size) {  
    double minDist = MAX;  
    vector<Point> sortedY(strip.begin(), strip.begin() + size);  
    sort(sortedY.begin(), sortedY.end(), cmpY);  
  
    for (int i = 0; i < size; ++i) {  
        for (int j = i + 1; j < size && sortedY[j].x - sortedY[i].x < minDist; ++j) {  
            double d = distance(sortedY[i], sortedY[j]);  
            if (d < minDist) {  
                minDist = d;  
            }  
        }  
    }  
    return minDist;  
}  
  
// 递归函数:计算最接近点对距离  
double Cpair(const vector<Point>& points, int left, int right, double& d) {  
    if (left == right) {  
        return d;  
    }  
    if (left + 1 == right) {  
        d = min(d, distance(points[left], points[right]));  
        return d;  
    }  
  
    int mid = left + (right - left) / 2;  
    double dl = Cpair(points, left, mid, d);  
    double dr = Cpair(points, mid, right, d);  
    d = min(d, min(dl, dr));  
  
    // 合并左右两侧的结果  
    vector<Point> strip;  
    for (int i = left; i <= right; ++i) {  
        if (fabs(points[i].x - points[mid].x) < d) {  
            strip.push_back(points[i]);  
        }  
    }  
    d = min(d, stripClosest(strip, int(strip.size())));  
    return d;  
}  
  
// 主函数:调用递归函数,并初始化相关参数  
double Cpair1(double a[], int n, double& d) {  
    vector<Point> points(n);  
    for (int i = 0; i < n; ++i) {  
        points[i].x = a[i];  
        points[i].index = i; // 存储原始数组的索引  
    }  
    sort(points.begin(), points.end(), cmpX);  
    return Cpair(points, 0, n - 1, d);  
}  
  
int main() {  
    int n;  
    double a[N];  
    double d = MAX;  
    cin >> n;  
    for (int i = 0; i < n; i++) {  
        cin >> a[i];  
    }  
  
    Cpair1(a, n, d);  
    cout << d << endl;  
  
    return 0;  
}

第7关:众数问题 

任务描述

给定含有n个元素的多重集合S,每个元素在S中出现的次数称为该元素的重数。多重集S中最大的元素称为众数。给定一个n个自然数组成的多重集合S,设计算法求其众数和重数。 例如:给出 S = [1,2,3,4,5,2,2] S其众数是2,重数是3

相关知识
  1. 排序统计法
  • 将集合S中的元素进行排序。
  • 排序后,数组相同的元素都会相邻出现。
  • 遍历数组,在遍历过程中记录出现过的重数最大的元素及其重数
  1. 分治法
  • 选取一个基准数,将比基准数小的放于左侧,比基准数大的放与右侧
  • 计算基准数的重数
  • 如果左半部分的元素个数大于基准数重数,递归求左半部分众数及其重数
  • 如果右半部分的元素个数大于基准数重数,递归求右部分众数及其重数
  • 三者中重数最大的是S的众数
编程要求
  • 本关的编程任务是用分治法编写求众数及其重数的函数
  • int GetMode(int a[],int p,int r,int &Count)
  • 返回值为数组a[p]~a[r]之间的众数,参数Count存放众数对应的重数。

主函数如下:

 
 
  1. #include<iostream>
  2. using namespace std;
  3. int main()
  4. {
  5. int n,Cnt,mode;
  6. cin>>n;
  7. int *a = new int[n];
  8. for(int i = 0; i < n; i ++)
  9. cin>>a[i];
  10. mode = getMode(a,0,n-1,Cnt);
  11. return 0;
  12. }
测试说明

正确完成函数编写即可。

开始你的任务吧,祝你成功!

答案:

void Partition(int a[], int p, int r,int x,int jizhun[])

{

    int left = p , right = r;

    while (left < right)

    {

        //从右边遍历找到第一个<=基准值的元素

        while (a[right] > a[x] && right != left)//要注意,左右指针前进的顺序,因为以第一个元素为基准值,故右指针先走

        {

            if (a[right] == a[x])//判断等于为基准值的

                jizhun[1]++;//基准值的重数

            right--;

        }

//从左边遍历找到第一个>基准值的元素

        while (a[left] <= a[x] && left != right)

        {

            if (a[left] == a[x])

                jizhun[1]++;

            left++;

        }

       

        //交换位置,使左边的元素<=基准值,右边的元素>基准值

        if (left != right)

        {

            int temp = a[left];

            a[left] = a[right];

            a[right] = temp;

        }

    }

    //基准值归位

    int temp = a[right];

    a[right] = a[x];

    a[x] = temp;

    jizhun[0] = right;//存基准值下标

}

//函数返回值:众数

int getMode(int a[], int p, int r, int& Cnt)

{

    int Cnt_m=1,Cnt_l = 1, Cnt_r = 1;//存左右边、基准值元素的重数

    int mod_m=0,mod_l = 0, mod_r = 0;//存左右、基准值众数

    int* jizhun = new int[2];//数组0存基准值下标,1存基准值重数

    jizhun[0] = 0; jizhun[1] = 1;//设置初始值

    //设置基准值为数组第一个元素

    Partition(a,p,r,p,jizhun);//数组第一个元素为基准值,对数组进行划分

    mod_m = a[p]; Cnt_m = jizhun[1];//基准值的重数和众数

    int leftsize = jizhun[0] - jizhun[1] + 1;//基准值左边元素个数

    int rightsize = r - jizhun[0];//基准值右边的元素个数

    if (leftsize > jizhun[1])//左边元素个数多,递归求左边元素

    {

        mod_l=getMode(a, p, (jizhun[0] - jizhun[1]), Cnt_l);

    }

    if (rightsize > jizhun[1])//右边元素个数多,递归求右边元素

    {

        mod_r=getMode(a, (jizhun[0] + 1), r, Cnt_r);

    }

    //比较三者重数--特殊情况没有考虑---》即重数均为1或者有两个数的重数相等,再加一部分比较重数相等情况下的众数即可,这里可以单独写一个比较函数.

    if (Cnt_l > Cnt_r)

    {

        if (Cnt_l > Cnt_m)

        {

            Cnt = Cnt_l;

            return mod_l;

        }

        else

        {

            Cnt = Cnt_m;

            return mod_m;

        }

    }

    else

    {

        if (Cnt_r > Cnt_m)

        {

            Cnt = Cnt_r;

            return mod_r;

        }

        else

        {

            Cnt = Cnt_m;

            return mod_m;

        }

    }

}

第8关:求逆序对数

任务描述

对于不同的排序可以用逆序来评价它们之间的差异。 一个排列含有逆序的个数称为这个排列的逆序数。例如排列 263451 含有8个逆序,(2,1),(6,3),(6,4),(6,5),(6,1),(3,1),(4,1),(5,1),因此,该排列的逆序数就是8。 显然,由1,2,...,nn个数组成的n!个排列中,最小逆序数是0,对应排列1,2,...,n。 最大逆序数是2n(n−1)​,对应排列n,n−1,...,2,1。 逆序数越大与原始排列差异越大。

编程要求

根据提示,在右侧编辑器补充代码。

输入格式

第一行是一个整数n,表示该排列有n个数(n<=100000)。

第二行是n个不同的正整数,之间以空格隔开,表示该排列。

输出格式

输出该排列的逆序数。

输入输出样例

测试输入:

6 2 6 3 4 5 1 预期输出: 8

提示

结合归并排序做


开始你的任务吧,祝你成功!

答案:

//归并排序及求逆序对
#include<iostream>
using namespace std;
#define N 1000005
int a[N], b[N];//b为辅助数组
long long cnt;
void merge_sort(int l, int r)
{
	// 如果整个区间中元素个数大于1,则继续分割
	if (r - l > 0)
	{
		// 1、尽量划分为数量相等的2个子序列,并排序
		int mid = (l + r) / 2;
		merge_sort(l, mid);
		merge_sort(mid + 1, r);

		// 2、将2个有序的序列合并成一个有序序列,在左右有序的子数组合并的同时,统计逆序对数
		/*********Begin***********/
          int i = l, j = mid + 1, k = l;  
        while (i <= mid && j <= r)  
        {  
            if (a[i] <= a[j])  
                b[k++] = a[i++];  
            else  
            {  
                b[k++] = a[j++];  
                // 计算逆序对数量,即左半部分中剩余的元素数量  
                cnt += mid - i + 1;  
            }  
        }  
        // 复制剩余的元素  
        while (i <= mid)  
            b[k++] = a[i++];  
        while (j <= r)  
            b[k++] = a[j++]; 

  
       /*********End***********/
		// 3、将辅助数组b中排好序的元素复制到a中
		for (i = l; i <= r; i++)
			a[i] = b[i];
	}
}
int main()
{
	int n;
	while (cin >> n)
	{
		for (int i = 1; i <= n; i++)
			cin >> a[i];
		cnt = 0;
		merge_sort(1, n);

		cout << cnt << endl;
	}
	return 0;
}

分治法-二分查找 

第1关:排序矩阵查找

题目描述

《程序员面试金典(第 6 版)》

任务描述

本关任务:给定M×N矩阵,每一行、每一列都按升序排列,请编写代码找出某元素。

示例:

现有矩阵 matrix 如下:

[1, 4, 7, 11, 15], [2, 5, 8, 12, 19], [3, 6, 9, 16, 22], [10, 13, 14, 17, 24], [18, 21, 23, 26, 30]

给定 target = 5,返回 true。

给定 target = 20,返回 false。。

提示

可以按每行二分查找,复杂度是O(mlgn) 也可以按每列二分查找,复杂度是O(nlogm)

编程要求

根据提示,在右侧编辑器补充代码。

测试说明

输入格式:第一行是三个正整数m,n,x.随后m行,每行n列 输出格式:true 或者 false

测试输入: 5 5 5 1 4 7 11 15 2 5 8 12 19 3 6 9 16 22 10 13 14 17 24 18 21 23 26 30 预期输出: Yes


开始你的任务吧,祝你成功!

答案:

#include<iostream> 
using namespace std;
const int N = 100;
int m,n;

bool binarySearch(int arr[], int target, int left, int right) {  
    while (left <= right) {  
        int mid = left + (right - left) / 2;  
        if (arr[mid] == target) return true;  
        else if (arr[mid] < target) left = mid + 1;  
        else right = mid - 1;  
    }  
    return false;  
}  
  
bool searchMatrix(int matrix[][N], int target) {  
    for (int i = 0; i < m; i++) {  
        // 每一行的最后一个元素如果小于目标值,那么目标值肯定不在这一行  
        if (matrix[i][n - 1] < target) continue;  
        // 每一行的第一个元素如果大于目标值,那么目标值肯定不在这一行  
        if (matrix[i][0] > target) break;  
        // 在当前行执行二分查找  
        if (binarySearch(matrix[i], target, 0, n - 1)) {  
            return true;  
        }  
    }  
    return false;  
}  
int main()
{
    int x;
    int matrix[N][N];
    cin>>m>>n>>x;
    for(int i = 0; i < m; i ++)
      for(int j = 0; j < n; j ++)
         cin>>matrix[i][j];

    if(searchMatrix(matrix,x))cout<<"true"<<endl;
    else cout<<"false"<<endl;
    return 0;

}

第2关:魔术索引 

题目来源

《程序员面试金典(第 6 版)》魔术索引

任务描述

本关任务:在数组A[0...n−1]中,有所谓的魔术索引,满足条件A[i]=i。给定一个有序整数数组,编写一种方法找出魔术索引,若有的话,在数组A中找出一个魔术索引,如果没有,则返回-1。(数据没有重复) 例如: 输入:nums = [0, 1, 3, 4, 5] 输出:0 说明: 0下标的元素为0,1的下标为1,0是满足条件索引值最小的一个。 输入:nums = [-1, 1, 3] 输出:1

编程要求

根据提示,在右侧编辑器补充代码。

测试说明

输入格式:输入两行,第一行一个正整数n,第二行n个有序正整数 输出格式:输出一个整数,魔术索引的下标或者-1.

测试输入: 4 -5 -2 0 3 预期输出: 3

提示:

  • 遍历一遍时间复杂度最高O(n)
  • 可以利用二分查找思想设计O(lgn)算法

开始你的任务吧,祝你成功!

答案:

#include<iostream>
using namespace std;
const int N = 100000;

int MagicIndex(int a[],int n)
{
/***********Begin*********/
for(int i=0;i<n;i++)
{
  if(i==a[i])
  return i;
}
return -1;

/***********End*********/
}
int main()
{
    int n,a[N],mi;
    cin>>n;
    for(int i = 0; i < n; i ++)
      cin>>a[i];
   mi = MagicIndex(a,n);
    cout<<mi<<endl;
    return 0;

}

第3关:搜索旋转数组 

题目来源

任务描述

本关任务:搜索旋转数组。给定一个排序后的数组,包含n个整数,但这个数组已被旋转过很多次了,次数不详。请编写代码找出数组中的某个元素,假设数组元素原先是按升序排列的。若有多个相同元素,返回索引值最小的一个。 示例: 输入: arr = [15, 16, 19, 20, 25, 1, 3, 4, 5, 7, 10, 14], target = 5 输出: 8(元素5在该数组中的索引)

相关知识

旋转数组分为左旋转和右旋转两类,下面是左旋转。 给定一个数组,将数组中的元素向左旋转 k 个位置,其中 k 是非负数。

,

原数组为 arr[] = [1,2,3,4,5,6,7] ,将其向左旋转 2 个元素的位置,得到数组 arr[] = [3,4,5,6,7,1,2]。

第一次旋转后得到的数组为 arr[] = {2,3,4,5,6,7,1};

第二次旋转后得到的数组为 arr[] = {3,4,5,6,7,1,2} 。

具体步骤如图 所示。

,

编程要求

输入格式:两行,第一行两个正整数,n和target;第二行是n个正整数 输出格式:正整数target在数组中的索引

测试说明

测试输入: 12 4 15 16 19 20 25 1 3 4 5 7 10 14 预期输出: 7


开始你的任务吧,祝你成功!

答案:


#include <iostream>  
using namespace std;  
const int N = 1000;  
  
int search(int a[], int l, int r, int target) {  
    if (l > r) return -1; // 如果没有找到目标值,返回-1  
  
    int mid = l + (r - l) / 2;  
  
    if (a[mid] == target) {  
        // 如果找到了目标值,需要向左搜索确保返回的是最小索引  
        while (mid > l && a[mid - 1] == target) {  
            mid--;  
        }  
        return mid;  
    }  
  
    // 判断目标值应该在mid的左边还是右边  
    if (a[l] <= a[mid]) { // 左半部分有序  
        if (target >= a[l] && target < a[mid]) {  
            // 目标值在左半部分  
            return search(a, l, mid - 1, target);  
        } else {  
            // 目标值在右半部分  
            return search(a, mid + 1, r, target);  
        }  
    } else { // 右半部分有序  
        if (target > a[mid] && target <= a[r]) {  
            // 目标值在右半部分  
            return search(a, mid + 1, r, target);  
        } else {  
            // 目标值在左半部分  
            return search(a, l, mid - 1, target);  
        }  
    }  
}  
  
int main() {  
    int n, target;  
    cin >> n >> target;  
    int a[N];  
    for (int i = 0; i < n; i++) { // 注意数组应该从0开始索引  
        cin >> a[i];  
    }  
  
    int index = search(a, 0, n - 1, target);  
    if (index != -1) {  
        cout << index << endl;  
    } else {  
        cout << -1 << endl;  
    }  
    return 0;  
}

第4关:寻找两个正序数组的中位数 

题目来源

力扣 题库 4.寻找两个正序数组的中位数 困难

任务描述

本关任务:给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的中位数 。算法的时间复杂度应该为 O(log (m+n)) 。 示例 1: 输入:nums1 = [1,3], nums2 = [2] 输出:2.00000 解释:合并数组 = [1,2,3] ,中位数 2

数据范围

0 <= m <= 1000 0 <= n <= 1000 1 <= m + n <= 2000 -1e6 <= nums1[i], nums2[i] <= 1e6

相关知识

在统计中,中位数被用来:将一个集合划分为两个长度相等的子集,其中一个子集中的元素总是大于另一个子集中的元素。

这其中又分为偶数组和奇数组: 奇数组: [2 3 5] 对应的中位数为3 偶数组: [1 4 7 9] 对应的中位数为 (4 + 7) /2 = 5.5

编程要求

根据提示,在右侧编辑器补充代码。

测试说明

输入格式:三行,第一行两个正整数m,n第二行和第三行分别m个和n个正序数组 输出格式:两个正序数组的中位数mid(保留1位小数)

测试输入4 3 1 4 7 9 2 3 5 预期输出: 4


开始你的任务吧,祝你成功!

答案:


#include <iostream>
using namespace std;

#define max(a,b) (((a) > (b)) ? (a) : (b))
#define min(a,b) (((a) < (b)) ? (a) : (b)) 
const int N = 1e5+10;

double findMedianSortedArrays(int nums1[], int nums2[],int n,int m) {
/************Begin**********/
int a[n+m+1], k = 0, i = 0, j = 0;

while(i<n&&j<m)
    {
        if(nums1[i]<nums2[j]) 
        {
            a[k++] = nums1[i++];

        }
        else 
        {
            a[k++] = nums2[j++];
        }
    }
    while(i<n)
    {
        a[k++] = nums1[i++];
    }
    while(j<m)
    {
         a[k++] = nums2[j++];
    }
    k = n+m;
    double s = 0;
    if(k%2==0)
    {
        s = a[k/2]+a[k/2-1];
        s /= 2;
    }
    else s = a[k/2];
 return s;

/************End**********/
}


int main()
{
    int m,n,nums1[N],nums2[N];
    cin>>n>>m;
    for(int i = 0; i < n; i ++)cin>>nums1[i];
    for(int i = 0; i < m; i ++)cin>>nums2[i];

	double ret = findMedianSortedArrays(nums1, nums2,n,m);
	cout<<ret<<endl;
	return 0;
}

动态规划课堂案例 

第1关:01背包问题

任务描述

本关任务:给定n种物品和一背包。物品i占的容量是vi​,其价值为wi​,背包的总容量为C。问应如何选择装入背包的物品,使得装入背包中物品的总价值最大

输入输出

输入格式:第一行两个整数n和c,物品个数和背包容量;随后n行表示每个物品的价值vi​,重量wi​ 输出格式:能达到的最大价值

输入样例 5 10 6 2 3 2 5 6 4 5 6 4

输出样例: 15


开始你的任务吧,祝你成功!

答案:

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1010;
 int v[N],w[N];
int solve(int n,int c)
{
/**********Begin***********/
int k[N][N];
for(int i=1;i<=n;i++)
{
    for(int j=0;j<=c;j++)
    {
      if(w[i]>j)
      {
      k[i][j]=k[i-1][j];
      }
      else
      {
          if(k[i-1][j]>k[i-1][j-w[i]]+v[i])
          k[i][j]=k[i-1][j];
          else
          k[i][j]=k[i-1][j-w[i]]+v[i];
      }
    }
   
}

 return k[n][c];

/**********End***********/
}

int main()
{
    int n,c;
	cin>>n>>c;
	for(int i = 1; i <= n; i ++)
	  cin>>v[i]>>w[i]; 
	cout<<solve(n,c)<<endl;
	return 0;
}

第2关:最长公共子序列 

任务描述

本关任务:一个序列,如果是两个或多个已知序列的子序列,且是所有子序列中最长的,则为最长公共子序列。给出两个序列X和Y,求其最长公共子序列长度。

相关知识

  1. 字符子串:指的是字符串中连续的n个字符,如abcdefg中,ab,cde,fg等都属于它的字串。

  2. 字符子序列:指的是字符串中不一定连续但先后顺序一致的n个字符,即可以去掉字符串中的部分字符,但不可改变其前后顺序。如abcdefg中,acdg,bdf属于它的子序列,而bac,dbfg则不是,因为它们与字符串的字符顺序不一致。

  3. 公共子序列:如果序列C既是序列A的子序列,同时也是序列B的子序列,则称它为序列A和序列B的公共子序列。如对序列 1,3,5,4,2,6,8,7和序列 1,4,8,6,7,5 来说,序列1,8,7是它们的一个公共子序列。

编程要求

根据提示,在右侧编辑器补充代码。

输入输出

输入格式:两行,每行一个字符串 输出格式:一个整数m,代表两个字符串的最长公共子序列长度

输入样例: BDCABA ACBDAB

预期输出: 4


开始你的任务吧,祝你成功!

第3关:矩阵连乘问题

任务描述

本关任务:给定n个矩阵

解释

A1​,A2​,…,An​,其中Ai​Ai+1​是可乘的,i=1,2…,n−1。如何确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。

测试说明

输入格式 第1行一个正整数n,是矩阵个数;第2行是n+1个正整数,表示他们的维数 输出格式 最少乘法次数

测试输入6 35 10 25 5 45 10 15 预期输出: 8625


开始你的任务吧,祝你成功!

答案:

#include <iostream>
using namespace std; 
const int N = 110;
int n;
int p[N],m[N][N];

int MatrixChain(int n)
{
/*******Begin********/

for(int i=1;i<=n;i++)
m[i][i]=0;
for(int len=2;len<=n;len++)
{
  for(int i=1;i<=n-len+1;i++)
  {
    int j=i+len-1;
    int cost;
    m[i][j]=m[i+1][j]+p[i-1]*p[i]*p[j];
    for(int k=i+1;k<j;k++)
    {
      cost=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];
      if(m[i][j]>cost)
      m[i][j]=cost;
    }
  }
}
return m[1][n];



/*******End********/
}

int main()
{
   int min;
    cin >> n;  
    for(int i = 0; i <= n; i++)
      cin>>p[i];
	
	min = MatrixChain(n);
    printf("%d",min);
	

	return 0;
}

第4关:凸多边形最优三角剖分 

任务描述

本关任务:给定凸多边形P,以及定义在由多边形的边和弦组成的三角形上的权函数w,设为

解释

w(vi​vj​vk​)=∣vi​vj​∣+∣vj​vk​∣+∣vi​vk​∣,要求确定该凸多边形的三角剖分,使得该三角剖分有最小的权之和。

编程要求

根据提示,在右侧编辑器补充代码。

测试说明

输入格式 第一行一个正整数,表示多边形顶点数; 输出格式 三角剖分的最小权值和 测试输入: 6 0 2 2 3 1 4 2 0 1 5 2 3 2 1 0 2 1 4 3 5 2 0 6 2 1 2 1 6 0 1 4 3 4 2 1 0 预期输出: 24


开始你的任务吧,祝你成功!

答案:

#include<iostream> 
using namespace std; 
const int N = 110;
int weight[N][N];//权值的邻接矩阵
int t[N][N];
int n;          //顶点个数


int w(const int a, const int b, const int c)
{//三角形权值函数
    return weight[a][b] + weight[b][c] + weight[c][a];
}

int minest_weight_val()
{
 /**********Begin**********/
 int mi=0;
 for(int i=1;i<=n;i++)
 t[i][i]=0;
 for(int r=2;r<=n;r++)
 {
   for(int i=1;i<=n-r+1;i++)
   {
     int j=i+r-1;
     t[i][j]=t[i+1][j]+w(i-1,i,j);
     for(int k=i+1;k<=i+r-1;k++)
     {
       int u=t[i][k]+t[k+1][j]+w(i-1,k,j);
       if(u<t[i][j])
       t[i][j]=u;
     }
     if(t[i][j]>mi)
     mi=t[i][j];
   }
 }
return mi;



  /**********End**********/
}
int main()
{
    int min;
    cin>>n;  
   for(int i = 0; i < n; i ++)
    for(int j = 0; j < n; j ++)
       cin >> weight[i][j];  
  min = minest_weight_val();
  cout<<min;
  return 0;

}

第5关:最优二叉搜索树 

任务描述

本关任务:有序集

解释

S={x1​,..,xn​}的二叉搜索树利用二叉树的节点来存储有序集中的元素。 二叉搜索树的性质

  1. 若它的左子树不空,则左子树上所有内节点的值均小于它的根节点的值;
  2. 若它的右子树不空,则右子树上所有内节点的值均大于它的根节点的值;
  3. 它的左、右子树也分别为二叉搜索树
  4. 二叉树的叶节点是形如

    解释

    xi​,xi+1​​的开区间。

假设有序集

解释

S={x1​,..,xn​}用二叉搜索树存储,每个内节点存储元素xi​,叶节点存储形如

解释

xi​,xi+1​​的开区间。

  1. 内节点xi​的存取概率bi​
  2. 叶节点

    解释

    xi​,xi+1​​的存取概率ai​
  3. 解释

    (a0​,b1​,a1​,…,bn​,an​)称为集合S的存取概率分布。

在所有表示S的二叉搜索树中找出一棵具有最小平均路长的二叉搜索树

相关知识

在表示S的二叉搜索树T中,设存储元素xi​的节点深度是ci​,叶节点

解释

(xi​,xi+1​​)的节点深度为di​,在T中进行一次搜索所需平均比较次数:

,

编程要求

根据提示,在右侧编辑器补充代码。

测试说明

输入格式:第一个行为正整数n,是集合元素个数;第二行是n个叶节点存取概率,第3行是n个内节点存储概率 输出格式:最优二叉搜索树的最小平均路长

测试输入: 6 0.05 0.10 0.05 0.05 0.05 0.10 0 0.15 0.1 0.05 0.1 0.2 预期输出: 2.35


开始你的任务吧,祝你成功!

答案:

#include<iostream> 
using namespace std; 
const int N = 50;
double a[N];//叶子节点概率数组
double b[N];//内节点概率数组
int n; //集合元素个数


double OptimalBinarySearchTree(int n)
{
/***********Begin*********/

double m[N][N];
double s[N][N];
double w[N][N];

for(int i = 0;i <= n;i++)
{
w[i+1][i] = a[i];
m[i+1][i] = 0;
s[i+1][i]=0;
}

for(int r = 0;r < n;r++)
{
   for(int i = 1;i <= n - r;i++)
  {
 int j = i + r;
 w[i][j] = w[i][j - 1] + a[j] + b[j];
 int p;
 if(s[i][j-1]>i)
  p=s[i][j-1];
  else
p=i;
 m[i][j] = m[i][p-1]+m[p+1][j];
 s[i][j] = p;
 for(int k = i + 1;k <= j;k++)
 {
double t = m[i][k - 1] + m[k + 1][j];
if(t <= m[i][j])
{
 m[i][j] = t;
s[i][j] = k;
}

}
m[i][j] += w[i][j];
}

}
return m[1][n];

/***********End********/

}
int main()
{
    double minPath;
  cin>>n; 
  for(int i = 0;i < n; i ++)
     cin>>a[i];
  for(int j = 0; j < n; j ++)
     cin>>b[j];
  minPath = OptimalBinarySearchTree(n-1);
  printf("%.2lf",minPath);

}

第6关:最大子段和 

任务描述

本关任务:给定由n个整数(可能为负数)组成的序列

解释

a1​,a2​ ,..,an​,求该序列形如

解释

∑k=1j​ak​的子段和的最大值。当所有整数均为负整数时,定义其最大字段和为0。例如:

,

编程要求

根据提示,在右侧编辑器补充代码。

输入输出

输入格式:第一行为正整数N,随后一个长度为n的整数序列 输出格式:最大子段和sum

测试输入: 6 -2 11 -4 13 -5 -23 预期输出: 20


开始你的任务吧,祝你成功!

答案:

#include<iostream> 
using namespace std; 
const int N = 1000010;
int a[N];
int n;

int MaxDPSum(){
	/******Begin********/
int sum=0,b=0;
for(int i=0;i<n;i++)
{
    if(b>0)
    b=b+a[i];
    else
    b=a[i];
    if(b>sum)

    sum=b;
}
return sum;



    /*******End********/
}


int main()
{
    int maxsum;
    cin>>n; 
    for(int i = 0; i < n; i ++)
        cin>>a[i];
    maxsum = MaxDPSum();
    cout<<maxsum<<endl;
    return 0;
}

动态规划进阶练习 

第1关:石子合并

任务描述

沿着河岸摆放 N 堆石子,现要将石子有次序地合并成一堆,规定每次只能选相邻的 2 堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。 例如:4堆石子4,5,9,4,可以按(((4,5),9),4)合并。

  • 第一次合并得分是9分,合并之后石子堆是9,9,4
  • 第二次合并得分是18分,合并之后石子堆是18,4
  • 第三次合并得分是22分,合并之后石子堆是22
  • 三次合并总得分49

试设计出一个算法,计算出将 N 堆石子合并成 1堆的最小得分和最大得分。

测试说明
输入格式

数据的第 1行是正整数 N,表示有N 堆石子。

2行有 N 个整数,第i 个整数 ai​ 表示第i 堆石子的个数。

输入格式

输出共 2 行,第 1 行为最小得分,第 2行为最大得分。

样例输入

4 4 5 9 4

样例输出

44 54

提示

1≤N≤100

解释

0≤ai​≤20

相关知识

编程要求

根据提示,在右侧编辑器编写函数,输出石子合并问题的最小得分和最大得分。


开始你的任务吧,祝你成功!

答案:

#include <iostream>
#include <algorithm>
using namespace std;
void dpStone(int a[],int n)
{
  int dp1[n+5][n+5],dp2[n+5][n+5];
  for(int i=1;i<=n;i++)
  {
    for(int j=1;j<=n;j++)
    {
      dp1[i][j]=0;
    }
  }
  for(int i=1;i<=n;i++)
  {
    for(int j=1;j<=n;j++)
    {
      dp2[i][j]=0;
    }
  }
  for(int len=2;len<=n;len++)
  {
    for(int i=1;i+len-1<=n;i++)
    {
      int j=i+len-1;
      dp1[i][j]=1e8;
      int sum=0;
      for(int k=i;k<=j;k++)
      {
        sum+=a[k];
      }
      for(int k=i;k<=j-1;k++)
      {
        dp1[i][j]=min(dp1[i][k]+dp1[k+1][j]+sum,dp1[i][j]);
        dp2[i][j]=max(dp2[i][k]+dp2[k+1][j]+sum,dp2[i][j]);
      }
    }
  }
  cout<<dp1[1][n]<<endl<<dp2[1][n];
}

第2关:基因检测 

任务描述

本关任务:找出两个串的最长公共子串的长度。

用一个字符串表示一段基因,例如:CTATGGGTTT。两段基因的相似度定义为它们所包含的最大公共子串的长度。

例如:CCTTGGTGGGC的最大公共子串为TGG,它的长度为3,则我们称CCTTGGTGGGC的相似度为3。 现给定两段基因,要求计算它们的相似度。

编程要求

在右侧编辑器中有一个函数Similar,它有两个参数str1str2,是两个字符串,长度不超过50

请你在这个函数中,计算并输出这两个字符串的相似度。

输入数据由评测系统读取,并传递给Similar函数。具体见测试说明。

测试说明

平台会对你编写的代码进行测试:

测试输入: CCCCC TTTTTGGGGGCC 预期输出: 2

测试输入: ACTGGG DDD 预期输出: 0

每组输入有两个,是两个字符串,分别对应str1str2


开始你的任务吧,祝你成功!

答案:

#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
 
void Similar(char *str1,char *str2)
{
	/**********   Begin   **********/
    int len1=strlen(str1),len2=strlen(str2);
    int a[len1][len2];
    for(int i=0;i<len1;i++){
        for(int j=0;j<len2;j++){
            if(str1[i]==str2[j]){
                a[i][j]=1;
            }
            else{
                a[i][j]=0;
            }
        }
    }
    int max=0;
    for(int i=0;i<len1;i++){
        for(int j=0;j<len2;j++){
            if(a[i][j]==1){
                int f=1,m=i+1,n=j+1;
                while(m<len1 && n<len2 && a[m][n]==1){
                    m++;
                    n++;
                    f++;
                }
                max=max>f?max:f;
            }
        }
    }
	printf("%d",max);
	
	
	/**********   End   **********/
}

第3关:药剂稀释 

任务描述

本关任务:找出一个序列中的最长下降子序列其中的元素个数。

医院里有一种药剂,其可以稀释成不同的浓度供病人使用,并且对于已知浓度的该药剂,使用时只能稀释不能增加浓度。

由于这种药需求量较大,同一瓶药剂只能给某个病人以及排在他后面的若干人使用。不同的病人对药物的浓度要求可能不同。

现在为了最大限度的利用每一瓶药剂(不考虑容量),已知n个病人所需药物浓度的序列,请计算出一瓶药剂能最多使用的人数。

编程要求

在右侧编辑器中有一个函数Cal,它有两个参数arrn

arr中包含n个病人对药物浓度的要求。

请你在这个函数中补充代码,计算并输出一瓶药剂能最多使用的人数。

输入数据由评测系统读取,并传递给Cal函数。具体见测试说明

测试说明

平台会对你编写的代码进行测试:

测试输入: 6 0.7 0.9 0.6 0.8 0.8 0.4

预期输出: 4

每组输入有两行,第一行有一个数n,第二行的n个数为数组的内容。


开始你的任务吧,祝你成功!

答案:

#include <iostream>
#include <algorithm>
using namespace std;
 
void Cal(double arr[],int n)
{
    /**********   Begin   **********/
	

      int a[n];
    int t=0;
    int max=0;
    for(int i=n-1;i>=0;i--)
    {
        a[i]=1;
        for(int j=i+1;j<n;j++)
        {
            if(arr[j]<=arr[i])
            {
            t=a[j]+1;
            if(a[i]<=t)
            a[i]=t;
            }
        }
        if(max<=a[i])
        max=a[i];
    }
	//补充代码完成任务
printf("%d",max);
	/**********   End   **********/
}

第4关:找相似串 

任务描述

本关任务:找出最接近的相似串。

一般情况下,度量两个串S1S2的相似性,可以通过从一个串变换成另一个串所需要的最少操作次数来衡量,需要的操作次数越少,则越相似。

假设从一个串变化成另一个串所允许的操作只有两种:插入一个字符或者删除一个字符。无论是插入还是删除一个符号,均算作一次操作。

现给你一个串S,和一个串的集合T,让你找出集合T中与S最相似的串。

编程要求

右侧编辑器中有一个函数Similar,请在这个函数中读取数据完成任务。

输入数据由学员处理。输入的第一行为一个串S,第二行是一个整数n,范围0 < n < 20,表示接下来集合T中的串的个数。接下来n行,每行为一个串。

:所有字符串的长度不超过50

请输出T中与S最相似的串,如果有多个就输出多个串(按照输入时的顺序),每个串占一行。具体见测试说明

测试说明

平台会对你编写的代码进行测试:

测试输入: abcd 4 abd abdc abed aebcd

预期输出: abd aebcd

提示: 对于第一个串abd,在b后插入一个c就可以变成abcd,操作次数为1次。 第二个串abdc,删除d后面的c,在d前面增加一个c,即可变成abcd,操作次数为2次。 第三个串abed,删除d前面的e,在d前面增加一个c,即可变成abcd,操作次数为2次。 第四个串aebcd,删除a后面的e即可变成abcd,操作次数为1次。


开始你的任务吧,祝你成功!

答案:

#include <iostream>
#include <cstring>
using namespace std;
const int MAX=60;
void Similar()
{
	/**********   Begin   **********/
	char s[MAX];
	int n,end;
	cin >> s>>n;//读取主串和子串个数
	int len_s = strlen(s);
	char arr[20][MAX];
	int caozuo[20];//存操作次数
	int dp[MAX][MAX];//用数组dp[i][j]表示,子串从1-i转换到主串的操作数。
	for (int i = 0; i < n; i++)//读取子串
	{
		cin>>arr[i];
	}	
	for (int i = 0; i < len_s; i++)
	{
		dp[0][i] = i;  //处理边界
	}
 
	for (int k = 0; k < n; k++)//第k个子串
	{
		int len = strlen(arr[k]);//子串长度
		//初始化
		for (int j = 0; j < len; j++)
			dp[j][0] = j;
 
		for (int i = 1; i < len_s; i++)//i为主串下标
		{
			for (int j = 1; j < len; j++)//j为子串下标
			{
				if (s[i] == arr[k][j])
					dp[i][j] = dp[i - 1][j - 1];
				else
					dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + 1;
			}
		}
		caozuo[k] = dp[len_s-1][len-1];//存每个子串的最小操作数
	}
	end = caozuo[0];
	for (int i = 1; i < n; i++)
		end = min(end, caozuo[i]);  //找到最小操作数
	for (int i = 0; i < n; i++)
	{
		if (caozuo[i] == end)
			cout << arr[i] << endl;  //输出对应串
	}
 
	/**********   End   **********/
}

第5关:聪明的寻宝人 

任务描述

本关任务:计算寻宝人所能带走的宝物的最大价值。

一个寻宝人在沙漠中发现一处神秘的宝藏,宝藏中共有n个宝物(n不超过20),每个宝物的重量不同,价值也不同,宝物i的重量是wi,其价值为vi

寻宝人所能拿走的宝物的总重量为mm不超过50)。请帮助寻宝人写一个程序,计算寻宝人能够获得的最大总价值。

编程要求

在右侧编辑器中有一个函数MaxValue,它有四个参数valuesweightsnm

valuesweights分别存放了n件宝物的价值重量m为寻宝人所能携带的最大重量。

请在这个函数中补充代码,计算并输出寻宝人所能获得的最大总价值。

输入数据由评测系统读取,并传递给MaxValue函数。具体见测试说明

测试说明

平台会对你编写的代码进行测试:

测试输入: 3 10 3 4 4 5 5 6

预期输出: 11

每组输入有多行,第一行有两个数nm,分别为宝石数量寻宝人载重。下面有n行数据,每一行有两个数,分别是宝石重量宝石价值


开始你的任务吧,祝你成功!

答案:

#include <iostream>

using namespace std;

int num[20][20];

void MaxValue(int values[], int weights[], int m, int n)

{
    /**********   Begin   **********/

    int jmax = min(weights[m] - 1, n);

    for (int j = 0; j <= jmax; j++) {
        num[m][j] = 0;

    }

    for (int j = weights[m]; j <= n; j++)

        num[m][j] = values[m];

    for (int i = m - 1; i > 1; i--) {
        jmax = min(weights[i] - 1, n);

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

            num[i][j] = num[i + 1][j];

        for (int j = weights[i]; j <= n; j++)

            num[i][j] = max(num[i + 1][j], num[i + 1][j - weights[i]] + values[i]);

    }

    num[1][n] = num[2][n];

    if (n >= weights[1]) {
        num[1][n] = max(num[1][n], num[2][n - weights[1]] + values[1]);

    }

    cout << num[1][n];
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/628268.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

相机模型的内参、外参

相机模型的内参、外参 文章目录 相机模型的内参、外参1. 针孔模型、畸变模型&#xff08;内参&#xff09;2. 手眼标定&#xff08;外参&#xff09; Reference 这篇笔记主要参考&#xff1a;slam十四讲第二版&#xff08;高翔&#xff09; 相机将三维世界中的坐标点&#xff…

2024年加密软件市场大比拼:谁将成为数据保护的新星

在2024年的加密软件市场&#xff0c;一场激烈的竞争正在上演。各大厂商纷纷推出自家的最新产品&#xff0c;旨在为用户提供更加安全、可靠的数据保护方案。在这场大比拼中&#xff0c;谁将成为数据保护的新星&#xff0c;引领市场的新潮流呢&#xff1f; 首先&#xff0c;我们…

收藏与品鉴:精酿啤酒的艺术之旅

啤酒&#xff0c;这一古老的酒精饮品&#xff0c;不仅是人们生活中的日常饮品&#xff0c;更是一种艺术和文化的载体。对于Fendi club啤酒而言&#xff0c;收藏与品鉴更是一门深入骨髓的艺术之旅。 Fendi club啤酒的收藏&#xff0c;不仅仅是简单的存放和保管&#xff0c;而是一…

阿里云域名备案流程

阿里云域名备案流程大致可以分为以下几个步骤&#xff0c;这些信息综合了不同来源的最新流程说明&#xff0c;确保了流程的时效性和准确性&#xff1a; UP贴心的附带了链接&#xff1a; 首次备案流程&#xff1a;ICP首次备案_备案(ICP Filing)-阿里云帮助中心 (aliyun.com) …

AUS GLOBAL 与皇家贝蒂斯在对战皇家马德里的比赛日举办现场体验活动

AUS Global 最近前往西班牙庆祝与皇家贝蒂斯的赞助合作&#xff0c;并获得了难忘的比赛日体验&#xff0c;包括在贵宾室中观看皇家贝蒂斯对阵皇家马德里的精彩比赛。 活动开始时&#xff0c;AUS Global 受邀来到皇家贝蒂斯主场贝尼托-比利亚马林体育场的独家 Showbox 贵宾室。…

2024深圳杯数学建模C题参考论文24页+完整代码数据解题

一、问题研究 24页参考论文&#xff1a; 【编译器识别】2024深圳杯C题24页参考论文1-3小问完整解题代码https://www.jdmm.cc/file/2710545/ 为了回答这些问题&#xff0c;我们需要进行一系列的编译实验、分析编译结果&#xff0c;并构建判别函数。以下是对这些问题的初步分析…

P9748 [CSP-J 2023] 小苹果:做题笔记

目录 P9748 [CSP-J 2023] 小苹果 思路 代码 P9748 [CSP-J 2023] 小苹果 P9748 [CSP-J 2023] 小苹果 思路 先写几个看看规律 题意我们能看出来是三个三个一组的&#xff0c;然后每次取走的都是三个里面的第一个。我们应该很容易想到如果一轮的总数是三的倍数的话&#xff0…

【AI智能体】零代码构建AI应用,全网都在喊话歌手谁能应战,一键AI制作歌手信息查询应用

欢迎来到《小5讲堂》 这是《文心智能体平台》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 文心智能体大赛背景创建应用平台地址快速构建【基础配置】…

win10远程连接设置

1&#xff09;开启远程连接 方式1&#xff1a; 控制面板-->系统和安全-->系统&#xff0c;点击【允许远程访问】 选择 允许远程连接到此计算机 方式2&#xff1a; WindowsR快捷键打开运行对话框&#xff0c;输入 sysdm.cpl 打开系统属性 选择 允许远程连接到此计算机 …

社交时代的象征:探索Facebook的文化影响

在当今社交媒体盛行的时代&#xff0c;Facebook作为其中的巨头之一&#xff0c;不仅是一个网络平台&#xff0c;更是社交文化的象征。本文将深入探讨Facebook在社交时代的文化影响&#xff0c;从用户行为到社会互动&#xff0c;从信息传播到文化交流&#xff0c;揭示其在塑造当…

VBA在Excel中部首组查字法的应用

VBA在Excel中部首组查字法的应用 文章目录 前言一、网站截图二、操作思路三、代码1.创建数据发送及返回方法2.创建截取字符串中的数值的方法3.获取部首对应的编码4.获取特定部首的汉字运行效果截图前言 使用汉语字典查生字、生词,多用拼音查字法和部首查字法。以前都是用纸质…

【合成孔径雷达】合成孔径雷达的多视角理解和时/频成像算法的统一解释

文章目录 一、什么是雷达成像&#xff08;1&#xff09;主要的遥感探测手段&#xff1a;光学、红外和雷达&#xff08;2&#xff09;从数学的角度&#xff1a;雷达成像主要研究什么&#xff1f;数据采集&#xff1a; y T x n yTxn yTxn信息提取&#xff1a; y − > x ? y…

用于WB的抗体一定能用来做IHC吗?

首先&#xff0c;我们来了解下抗原表位。由于蛋白可以折叠成三维结构。 所以抗原表位可以分成两种类型&#xff1a; 线性表位 一般指的是由序列上相连接的一些氨基酸残基通过共价键形成的结构&#xff0c;也称为顺序表位&#xff0c;是蛋白质的一级结构&#xff0c;比较稳定&…

MySQL:MySQL索引结构为什么选用B+树?

一、前言 当我们发现SQL执行很慢的时候&#xff0c;自然而然想到的就是加索引。在MySQL中&#xff0c;无论是Innodb还是MyIsam&#xff0c;都使用了B树作索引结构。我们知道树的分类有很多&#xff0c;MySQL中使用了B树作索引结构&#xff0c;这是为什么呢&#xff1f; 本文将从…

企业计算机服务器中了rmallox勒索病毒怎么解密,rmallox勒索病毒解密工具流程

在当今数字化时代&#xff0c;越来越多的企业依赖计算机服务器进行办公开展业务&#xff0c;计算机服务器犹如企业的心脏&#xff0c;能够为企业存储许多重要的核心信息&#xff0c;帮助企业有效的开展各项工作业务&#xff0c;提高企业的生产效果&#xff0c;但网络是一把双刃…

tomcat--安装

官网&#xff1a;Apache Tomcat - Welcome! 官网文档&#xff1a;Apache Tomcat 8 (8.5.100) - Documentation Index 帮助文档&#xff1a;Apache Tomcat Home - Apache Tomcat - Apache Software Foundation FAQ - Apache Tomcat - Apache Software Foundation yum安装 查…

盘点那些年我们一起玩过的网络安全工具

一、反恶意代码软件 1.Malwarebytes 这是一个检测和删除恶意的软件&#xff0c;包括蠕虫&#xff0c;木马&#xff0c;后门&#xff0c;流氓&#xff0c;拨号器&#xff0c;间谍软件等等。快如闪电的扫描速度&#xff0c;具有隔离功能&#xff0c;并让您方便的恢复。包含额外…

Mysql 事务隔离级别

前言 在数据库管理系统中&#xff0c;事务&#xff08;Transaction&#xff09;是保证数据一致性和完整性的重要机制。在并发环境下&#xff0c;多个事务同时操作相同的数据可能会引发各种问题&#xff0c;如脏读、不可重复读、幻读等。为了解决这些问题&#xff0c;MySQL提供…

深入理解指针(2)

在上一篇深入理解指针(1)中我们已经初步了解指针地址&#xff1b;指针的解引用&#xff1b;指针变量类型作用&#xff0c;指针运算等知识&#xff0c;接下来我们将继续学习指针的相关内容&#xff0c;一起加油吧&#xff01;&#xff01;&#xff01; 1. 数组名的理解 在之前的…

AI绘画:Stable Diffusion 终极炼丹宝典:从入门到精通

一、为什么要学习使用Stable Diffusion&#xff1f; 1.1 Stable Diffusion能干嘛&#xff1f;它是有多强大&#xff1f; Stable Diffusion的应用领域包括&#xff1a;真人AI美女&#xff0c;生成头像、壁纸、绘画辅助 我相信各位在浏览视频时&#xff0c;多多少少已经见过许多…