叕三次算法作业

目录

A:数字三角形

B: 最长公共子序列

C: 0-1背包问题

D: 最长升序子序列

E: 恢复字符串

F: 复制书稿

G: 最大子段和

H: 称币问题

I: 矩阵连乘问题


A:数字三角形

题目描述

        给定每行由数字组成的数字三角形,寻找从顶至低某处的一条路径,使该路径所经过的数字的总和最大,每一步只能向左下或右下走。

输入

        输入有若干测试数据。每一组测试数据的第一行为一个整数H,表示有高度为H的数字三角形,接下来的H行描述一个数字三角形,其中的第i行有i个整数,整数之间用一个空格分开。当输入时表示数字三角形的高度的整数为0时,表示输入结束。

输出

        对每组数据,输出该组数字三角形从顶至低边某处的一条最优路径所经过的最大数字之和。

样例输入

5

7

3 8

8 1 0

2 7 4 4

4 5 2 6 5

4

6

2 1

4 3 4

1 2 3 4

0

样例输出

30

15

#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
    int height;

    while (std::cin >> height)
    {
        if (height == 0)
            return 0;
        std::vector<std::vector<int>> nums(height + 1, std::vector<int>(height + 1, 0));
        std::vector<std::vector<int>> result(height + 1, std::vector<int>(height + 1, 0));
        for (int i = 1; i <= height; i++)
        {
            for (int j = 1; j <= i; j++)
            {
                std::cin >> nums[i][j];
            }
        }
        for (int i = 1; i <= height; i++)
        {
            for (int j = 1; j <= i; j++)
            {
                if(j!=i && j!=1)
                {
                    result[i][j] = std::max(result[i - 1][j], result[i - 1][j - 1]) + nums[i][j];
                }
                else if( j == 1)//因为初始化0,防止负数取零问题
                {
                    result[i][j] = result[i-1][j] + nums[i][j];
                }
                else if (j == i)
                {
                    result[i][j] = result[i-1][j-1] + nums[i][j];
                }
            }
        }

        std::cout << *std::max_element(result[height].begin()+1, result[height].end()) << std::endl;
    }
    return 0;
}

B: 最长公共子序列

题目描述

序列Z=<B,C,D,B>是序列X=<A,B,C,B,D,A,B>的子序列,相应的递增下标序列为<2,3,5,7>。

一般地,给定一个序列X=<x1,x2,…,xm>,则另一个序列Z=<z1,z2,…,zk>是X的子序列,是指存在一个严格递增的下标序列〈i1,i2,…,ik〉使得对于所有j=1,2,…,k,Z中第j个元素zj与X中第ij个元素相同。

给定2个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列。

你的任务是:给定2个序列X、Y,求X和Y的最长公共子序列Z的长度。

输入

输入中的第1行是一个正整数T,(0<T<=10),表示有T组测试数据。接下来是每组测试数据的描述,每组测试数据有3行。

测试数据的第1行有2个正整数m、n,中间用一个空格隔开,(0<m,n<50);第2、3行是长度分别为m、n的2个序列X和Y,每个序列的元素间用一个空格隔开。序列中每个元素由字母、数字等构成。

输入直到文件结束。

输出

对输入中的每组测试数据,输出2行。先在一行上输出“Case #”,其中“#”是测试数据的组号(从1开始),再在第2行上输出这2个序列X、Y的最长公共子序列Z的长度。

样例输入

2

7 6

A B C B D A B

B D C A B A

8 9

b a a b a b a b

a b a b b a b b a

样例输出

Case 1

4

Case 2

6

#include <iostream>
#include <vector>
int main()
{
    int T;
    int count = 1;
    std::cin >> T;
    while (T--)
    {
        // 字符序列长度
        int len_x, len_y;
        std::cin >> len_x >> len_y;

        std::vector<char> nums_x(len_x + 1); // 字符序列x构成的字符数组
        std::vector<char> nums_y(len_y + 1); // 字符序列y构成的字符数组

        // 输入字符序列x
        for (int i = 1; i <= len_x; i++)
        {
            std::cin >> nums_x[i];
        }
        // 输入字符序列y
        for (int i = 1; i <= len_y; i++)
        {
            std::cin >> nums_y[i];
        }
        // 动态规划数组
        std::vector<std::vector<int>> dp(len_x + 1, std::vector<int>(len_y + 1));
        for (int i = 1; i <= len_x; i++)
        {
            for (int j = 1; j <= len_y; j++)
            {
                if (nums_x[i] == nums_y[j]) // 字符相同
                {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                }
                else // 字符不相同
                {
                    dp[i][j] = std::max(dp[i][j - 1], dp[i - 1][j]);
                }
            }
        }

        std::cout << "Case " << count++ << "\n";
        std::cout << dp[len_x][len_y] << "\n";
    }
}

C: 0-1背包问题

题目描述

给定n个物品和一个背包,物品i的重量为wi,价值为vi(i=1,2,……,n),背包能容纳的物品的重量为c,要从这n个物品中选出若干件放入背包,使得放入物品的总重量不超过c,而总价值达到最大,并找出一种放物品的方案。

输入

输入有若干组测试数据(不超过20组)。

每组测试数据有3行:其第1行上有2个整数n和c,分别是物品个数n和背包所能容纳物品的重量,(n<=50,c<=500),第2行上有n个整数v1、v2、…、vn,依次是n个物品的价值,第3行上有n个整数w1、w2、…、wn,,分别是n个物品的重量。诸整数之间用一个空格分开。

输入直到文件结束。

输出

现要求对输入中的每组测试数据,输出2行:

在第1行上输出“Case #”,其中“#”是测试数据的组号(从1开始)。

在第2行上输出可以放在包中的物品的最大价值。

样例输入

3 4

1 3 4

3 1 4

5 10

6 3 5 4 6

2 2 6 5 4

样例输出

Case 1

4

Case 2

15

#include <iostream>
#include <vector>
int main()
{
    int num, capacity;
    int count = 1;
    while (std::cin >> num >> capacity)
    {
        std::vector<int> values(num + 1);  // 价值数组,存放每个物品的价值
        std::vector<int> weights(num + 1); // 重量数组,存放每个物品的重量
        // 输入每个物品的价值
        for (int i = 1; i <= num; i++)
        {
            std::cin >> values[i];
        }
        // 输入每个物品的重量
        for (int i = 1; i <= num; i++)
        {
            std::cin >> weights[i];
        }
        // 动态规划数组,初始化为0
        std::vector<std::vector<int>> dp(num + 1, std::vector<int>(capacity + 1, 0));

        for (int i = 1; i <= num; i++)
        {
            for (int j = capacity; j >= 1; j--)
            {
                if (j >= weights[i]) // 可以装入第i件物品
                {
                    // 取装入第i件物品和不装入第i件物品的最大价值
                    dp[i][j] = std::max(dp[i - 1][j], dp[i - 1][j - weights[i]] + values[i]);
                }
                else // 不可以装入第i件物品
                {
                    dp[i][j] = dp[i - 1][j];
                }
            }
        }
        std::cout << "Case " << count++ << "\n";
        std::cout << dp[num][capacity] << "\n";
    }
}

D: 最长升序子序列

    题目描述

    一个数的序列b[1..m],当b1 < b2 < ... < bm的时候,称这个序列是严格递增(或上升)的,简称是上升序列。

    对于给定的一个序列(a1, a2, ..., an),可以得到一些上升的子序列(ai1, ai2, ..., aim),这里1 <= i1 < i2 < ... < im <= n。

    比如,对于序列(1, 7, 3, 5, 9, 4, 8),它有一些上升子序列,如(1, 7), (1, 3, 4, 8)等等。这些子序列中最长的长度是4,比如子序列(1, 3, 5, 8)。

    你的任务,就是对于给定的序列,求出最长上升子序列的长度。

    输入

    有多组数据。每组数据有2行,第一行是一个整数n(0<=n<=100),表示原始序列的个数,第二行上有n个整数a1, a2, ..., an(0<=ai<=100)。

    输出

    对每组测试数据,输出最长上升子序列的长度。

    样例输入

    3

    25 36 23

    3

    2 2 5

    样例输出

    2

    2

#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
    int n;
    int max_len = 0;
    while (std::cin >> n)
    {
        std::vector<int> nums(n + 1, 0);
        std::vector<int> result(n + 1, 1);//到每个元素的最长升序子序列至少是1
        for (int i = 1; i <= n; i++)
        {
            std::cin >> nums[i];
        }

        for(int i = 1; i<=n;i++)
        {
            for(int j= 1;j<i;j++)
            {
                if(nums[j] < nums[i])
                {
                    result[i] = std::max(result[j] + 1,result[i]);
                }
            }
        }

        max_len = std::max(max_len,*std::max_element(result.begin(),result.end()));
        std::cout<<max_len<<"\n";
    }
}

E: 恢复字符串

    题目描述

    有一个字符串被分成了两行字符串,其中第一行每个字符依次是原字符串所有奇数位置的字符,第二行每个字符依次是原字符串所有偶数位置的字符。

    现在给你任意两个字符串O、E,O包含奇数位置的字符,E包含偶数位置的字符,请你恢复原有字符串。

    输入

    有多组测试数据。第一行是一个整数n,0<n<10。

    接着是n组数据的描述,每组两行。第一行为字符串O,第二行为字符串E,字符串O和E的长度不超过50,且O与E的长度或相等或多一。

    输出

    对每组测试数据,一行输出原有字符串。

    样例输入

    2

    xyz

    abc

    12345

    abcd

    样例输出

    xaybzc

    1a2b3c4d5

#include<iostream>
#include<iomanip>

int main()
{
    int n;

    std::cin>>n;
    getchar();//去掉额外回车
    while(n--)
    {
        std::string str_1,str_2;

        std::getline(std::cin,str_1);
        std::getline(std::cin,str_2);
        
        std::string result;

        for(int i = 0;i<str_2.length();i++)//依据短的衔接
        {
            result += str_1[i];
            result += str_2[i];
        }

        if(str_1.length() != str_2.length())//不等说明str_1多一个,加上多的一个
        {
            result += str_1[str_1.length()-1];
        }

        std::cout<<result<<"\n";
    }
}

F: 复制书稿

    题目描述

    假设有m本书(编号为1,2,…,m),想将每本复制一份,m本书的页数可能不同(分别是P1,P2,…Pm)。

    任务是将这m本书分给k个抄写员(k〈=m〉,每本书只能分配给一个抄写员进行复制,而每个抄写员所分配到的书必须是连续顺序的。

    意思是说,存在一个连续升序数列 0 = b[0] < b[1] < b[2] < … < b[k-1] < b[k] = m,这样,第i号抄写员得到的书稿是从b[i-1]+1到第bi本书。

    复制工作是同时开始进行的,并且每个抄写员复制的速度都是一样的。所以,复制完所有书稿所需时间取决于分配得到最多工作的那个抄写员的复制时间。

    试找一个最优分配方案,使分配给这k个抄写员抄写这m本书时,完成所有抄写任务的时间最小。

    输入

    输入由若干个测试数据组成。每组测试数据有两行,第一行两个数是m、k:表示书本数目与抄写人数;第二行是m个由空格分隔的整数,表示m本书中每本的页数。

    输出

    对第i组测试数据,输出一行。先输出“Case i:”,接着输出这k个抄写员抄写这m本书时,完成所有抄写任务的最小时间。

    样例输入

    9 3

    1 2 3 4 5 6 7 8 9

    样例输出

    Case 1:17

#include<iostream>
#include<vector>
#include <numeric>

bool IsTheIntervalSumReasonable(int& max_TheSumOfInterval, std::vector<int>& book, int& num_book,
    int& num_person, std::vector<int>& leftmost, std::vector<int>& rightmost)
{
    int sum = 0;
    int count = 1;
    
    rightmost[count] = num_book;

    for(int i = num_book; i>= 1;i--)
    {
        if(sum + book[i] <= max_TheSumOfInterval)
        {
            sum += book[i];
        }
        else
        {
            leftmost[count] = i+1;
            rightmost[++count] = i;
            sum = book[i];
        }
    }

    leftmost[count] = 1;

    return count <= num_person;
}

int GetMinTime(std::vector<int>& book, int& num_book, int& num_person)
{
    int left = 1;
    int right = std::accumulate(book.begin(),book.end(),0);

    std::vector<int> leftmost(num_book +2,0),rightmost(num_book+2,0);

    while(left <= right)
    {
        int mid = (left + right)/2;

        if(IsTheIntervalSumReasonable(mid, book, num_book, num_person, leftmost, rightmost))
        {
            right = mid -1;
        }
        else 
        {
            left = mid + 1;
        }
    }

    IsTheIntervalSumReasonable(left, book, num_book, num_person, leftmost, rightmost);

    int max_Time = 0;
    for(int i =1;i <=num_person;i++)
    {
        max_Time = std::max(std::accumulate(&book[leftmost[i]],&book[rightmost[i]+1],0),max_Time);
    }

    return max_Time;
}


int main()
{
    int num_book, num_person, count = 1;

    while(std::cin>>num_book >> num_person)
    {
        std::vector<int> book(num_book+1);

        for(int i = 1;i<= num_book;i++)
        {
            std::cin>>book[i];
        }

        std::cout<<"Case " << count++ << ":" <<GetMinTime(book, num_book, num_person)<<"\n";
    }

    return 0;
}

G: 最大子段和

    题目描述

    最大子段和问题又叫最大子数列问题。该问题的目标是在一个数列中找到一个连续的子数列,使该子数列的和最大。例如,对数列−2, 1, −3, 4, −1, 2, 1, −5, 4,其连续子数列中和最大的是4,−1, 2, 1,其和为6。

    你的任务,就是对于给定的数列,求出其中的一个子数列使其和最大。

    输入

    有多组数据。每组数据有2行,第一行是一个整数n(0<=n<=100),表示数列的长度,第二行上有n个整数a1, a2, ..., an(-100<=ai<=100),其中至少数有一个非负。

    输出

    对每组测试数据,输出它的最大子段和值。

    样例输入

    6

    -2 11 -4 13 -5 -2

    8

    4 -2 5 -1 5 6 -3 -50

    样例输出

    20

    17

#include<iostream>
#include<vector>
#include<algorithm>

int main()
{
    int n;

    while(std::cin>>n)
    {
        std::vector<int > nums(n+1,0);
        std::vector<int > result(n+1,0);
        for(int i=1;i<= n;i++)
        {
            std::cin>>nums[i];
        }

        for(int i = 1;i<=n;i++)
        {
            result[i] = std::max(nums[i],nums[i]+result[i-1]);
        }

        std::cout<<*std::max_element(result.begin(),result.end())<<"\n";
    }
}

H: 称币问题

    题目描述

    在n个银币中有一个是不合格的,不合格的银币比合格银币要轻。现用天平秤银币,找出不合格的银币,且在最坏情况下秤银币的次数最少。

    输入

    输入有若干行。每行上有一个整数n(n>0),表示银币个数,n£100000。

    输出

    对输入的整数n,输出2行。第1行输出n的值,第2行上先输出“Times:”,接着输出在最坏情况下秤n个银币的最少次数。

    样例输入

    4

    7

    50

    样例输出

    4

    Times:2

    7

    Times:2

    50

    Times:4

#include<iostream>
#include<cmath>

int main()
{
    int n;
    while(std::cin>>n)
    {
        int nums[n+1];
        nums[1] = 1;
        nums[2] = 1;
        nums[3] = 1;
        
        for(int i = 4;i<=n;i++)
        {
            nums[i] = nums[(int)ceil(i/3.0)] + 1;
        }
        if(n==1)
        {
            std::cout<< n <<"\n"<<"Times:"<<1<<"\n";
            continue;
        }
        //int result = (int)ceil(log((double)n) / log(3.0));
        std::cout<< n <<"\n"<<"Times:"<<nums[n]<<"\n";
    }
}

I: 矩阵连乘问题

    题目描述

    给定n个矩阵A1,A2,…,An,其中,Ai与Aj+1是可乘的,i=1,2,…,n-l。

    你的任务是要确定矩阵连乘的运算次序,使计算这n个矩阵的连乘积A1A2…An时总的元素乘法次数达到最少。

    例如:3个矩阵A1,A2,A3,阶分别为10×100、100×5、5×50,计算连乘积A1A2A3时按(A1A2)A3所需的元素乘法次数达到最少,为7500次。

    输入

    测试数据有若干组,每组测试数据有2行。

    每组测试数据的第1行是一个整数n,(0<n<20),第2行是n+1个正整数p0、p1、p2、…、pn,这些整数不超过100,相邻两个整数之间空一格,他们表示n个矩阵A1,A2,…,An,的阶pi-1´pi,i=1,2,…,n。

    输出

    对输入中的每组测试数据,输出2行。先在一行上输出“Case #”,其中“#”是测试数据的组号(从1开始),再在第2行上输出计算这n个矩阵的连乘积A1A2…An时最少的总的元素乘法次数。

    样例输入

    3

    10 100 5 50

    4

    50 10 40 30 5

    样例输出

    Case 1

    7500

    Case 2

    10500

#include <bits/stdc++.h>

using namespace std;

int GetMinTimesOfMultiplication(vector<int>& matrix, int& n) {
    vector<vector<int>> dp(n + 1, vector<int>(n + 1, 0));
    // vector<vector<int>> splitPoint(n + 1, vector<int>(n + 1, 0));
    // dp是最优值,splitPoint是最优值的断开点的索引,n为题目所给的矩阵的个数
    // 矩阵段长度为1,则dp[][]中对角线的值为0,表示只有一个矩阵,没有相乘的

    for (int r = 2; r <= n; r++) { // 对角线循环,r表示矩阵的长度(2,3…逐渐变长)
        for (int i = 1; i <= n - r + 1; i++) { // 行循环
            // 从第i个矩阵Ai开始,长度为r,则矩阵段为(Ai~Aj)
            int j = r + i - 1; // 列的控制,当前矩阵段(Ai~Aj)的起始为Ai,尾为Aj
            // 求(Ai~Aj)中最小的,其实k应该从i开始,但先记录第一个值,k从i+1开始,这样也可以
            // 例如对(A2~A4),则i=2,j=4,下面一行的dp[2][4]=dp[3][4]+matrix[1]*matrix[2]*matrix[4],即A2(A3A4)
            dp[i][j] = dp[i + 1][j] + matrix[i - 1] * matrix[i] * matrix[j];
            // splitPoint[i][j] = i; // 记录断开点的索引

            // 循环求出(Ai~Aj)中的最小数乘次数
            for (int k = i + 1; k < j; k++) {
                // 将矩阵段(Ai~Aj)分成左右2部分(左dp[i][k],右dp[k+1][j]),
                // 再加上左右2部分最后相乘的次数(matrix[i-1] *matrix[k]*matrix[j])
                int temp = dp[i][k] + dp[k + 1][j] + matrix[i - 1] * matrix[k] * matrix[j];

                if (temp < dp[i][j]) {
                    dp[i][j] = temp;
                    // splitPoint[i][j] = k; // 保存最小的,即最优的结果
                }
            }
        }
    }

    return dp[1][n];
}

int main(int argc, char const *argv[]) {
    int n, cnt = 1;

    while (cin >> n) {
        vector<int> matrix(n + 1);
        
        for (int i = 0; i < n + 1; ++i) {
            cin >> matrix[i];
        }

        cout << "Case " << cnt++ << endl << GetMinTimesOfMultiplication(matrix, n) << endl;
    }

    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值