HNUCM-2022年秋季学期《算法分析与设计》练习12

目录

问题 A: 最长不下降子序列

问题 B: XP的点滴

问题 C: 0-1背包问题

问题 D: 安置路灯

问题 E: 最少硬币

问题 F: 图书排序

问题 G: 道具的魅力值

问题 H: 团结军团


问题 A: 最长不下降子序列

题目描述

给定一个序列 a,求去除 a 中一段连续长度为 L 的序列后,a 的最长不下降子序列的长度的最大值。

输入

单组数据。
第一行两个整数 n,L 表示序列的长度为 n,L 如题意所示。
第二行 n 个数表示序列 a
n ≤ 105, 0 ≤ L ≤ n

输出

输出一个整数表示最长不下降子序列长度的最大值

思路:不会,抄的c++代码

c++:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int t[2][maxn],dp[maxn],dn[maxn],n,L,ans=0;
struct Node {int val,num;} a[maxn];
bool cmp(Node a,Node b){
    return a.val<b.val;
}
bool cmp1(Node a,Node b){
    return a.num<b.num;
}
  
  
// T[x] 表示目前以数值 x 结尾的最大长度;t[0][x]对应dp[i]的树状数组,t[1][x]对应dn[i]的树状数组
int query(int p,int x){
    int cnt = 0;
    while(x)  cnt=max(cnt,t[p][x]), x-=x&-x;
    return cnt;
}
int update(int p,int x,int m){
    while(x<=n) t[p][x]=max(t[p][x],m), x+=x&-x;
}
int main()
{
    scanf("%d%d",&n,&L);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i].val);
        a[i].num = i;
    }
    sort(a+1,a+1+n,cmp);
    a[1].val = 1;
    for(int i=2,k=1;i<=n;i++){
        if(a[i].val==a[i-1].val) a[i].val = k;
        else a[i].val = ++k;
    }
    sort(a+1,a+1+n,cmp1);
    for(int i=1; i<=n; i++){
        dp[i] = query(0,a[i].val) +1;   //每次遍历的时候用 dp[i] 去查询更新当前的 T[a[i]]
        update(0,a[i].val,dp[i]);
        if(i>L){
            dn[i] = query(1,a[i].val) +1;
            update(1,a[i].val,dn[i]);   //在已截取的基础上寻找LIS
            update(1,a[i-L].val,dp[i-L]);   //截取当前元素的前L个
            ans = max(ans,dn[i]);
        }
    }
    for(int i=1;i<=n-L;i++){    //如果截取的L在最优LIS后面,取出来没有截取的就行
        ans=max(ans,dp[i]);
    }
    printf("%d\n",ans);
    return 0;
}

问题 B: XP的点滴

题目描述

XP一不留神感冒了,于是跑到校医院打点滴。打点滴真是无聊啊,他看到盐水一滴一滴地滴下来,突然想到一个问题:如果盐水有规律地滴下,先滴一滴,停一下;然后滴二滴,停一下;再滴三滴,停一下...,假设这瓶盐水一共有n毫升,每一滴是y毫升,每一滴需要的时间是一秒(假设最后一滴不到y毫升,需花费的时间也算一秒),停一下的时间也是一秒。请问XP多久能挂完这瓶盐水呢?

输入

单组输入数据 

n y (0<n,y<=1000)

输出

输出一行结果

思路:简单的计算一下要滴多少次,然后模拟一下打点滴一滴、两滴等等直到滴完。

while True:
    try:
        n, y = map(int, input().split())
        res = 0
        a, b = divmod(n, y)
        a = a + 1 if b != 0 else a
        for i in range(1, 1001):
            if a > i:
                res, a = res + i + 1, a - i
            else:
                res += a
                break
        print(res)
    except:
        break

问题 C: 0-1背包问题

题目描述

给定n种物品和一个背包,物品i的重量是Wi,其价值为Vi,背包的容量为C。如何选择装入背包的物品,可以使得装入背包中物品的总价值最大?

输入

每组输入包括三行,
第一行包括物品个数n,以及背包容量C。
第二、三行包括两个一维数组,分别为每一种物品的价值和重量。

输出

输出包括两行,第一行为背包的最大总价值,第二行为所选取的物品。
例如:最大总价值=15,物品选取策略为11001。数据保证答案唯一。

思路:经典的0-1背包问题,解决方案从dp[n][c]开始倒推即可,用s列表记录是否选取(dp[n][c]!=dp[n-1][c]说明选取了物品n)。

while True:
    try:
        n, c = map(int, input().split())
        v, w = list(map(int, input().split())), list(map(int, input().split()))
        dp, s = [[0] * (c + 1) for _ in range(n + 1)], ['0'] * n
        for i in range(1, n + 1):
            for j in range(c + 1):
                if w[i - 1] > j:
                    dp[i][j] = dp[i - 1][j]
                else:
                    dp[i][j] = max(dp[i - 1][j - w[i - 1]] + v[i - 1], dp[i - 1][j])
        print(dp[n][c])
        p = c
        for i in range(n, 0, -1):
            if dp[i][p] != dp[i - 1][p]:
                s[i - 1], p = '1', p - w[i - 1]
        print("".join(s))
    except:
        break

问题 D: 安置路灯

题目描述

小Q正在给一条长度为n的道路设计路灯安置方案。

为了让问题更简单,小Q把道路视为n个方格,需要照亮的地方用'.'表示, 不需要照亮的障碍物格子用'X'表示。

小Q现在要在道路上设置一些路灯, 对于安置在pos位置的路灯, 这盏路灯可以照亮pos - 1, pos, pos + 1这三个位置。

小Q希望能安置尽量少的路灯照亮所有'.'区域, 希望你能帮他计算一下最少需要多少盏路灯。

输入

输入的第一行包含一个正整数t(1 <= t <= 1000), 表示测试用例数
接下来每两行一个测试数据, 第一行一个正整数n(1 <= n <= 1000),表示道路的长度。
第二行一个字符串s表示道路的构造,只包含'.'和'X'。

输出

对于每个测试用例, 输出一个正整数表示最少需要多少盏路灯。

思路:利用贪心的思想,只要index处需要照亮,那么就在index+1处安装路灯,照亮index、index+1、index+2,然后从index+3开始寻找下一处需要照亮的地方。

t = int(input())
while t > 0:
    t, n, res, index, load = t - 1, int(input()), 0, 0, input().strip()
    while index < n:
        if load[index] == '.':
            res, index = res + 1, index + 3
        else:
            index = index + 1
    print(res)

问题 E: 最少硬币

题目描述

假设有4种硬币,它们的面值分别为1分、5分、10分和25分。
现在要找给顾客n分钱。
请问怎样找零钱才能使给顾客的硬币个数最少?
输出所需最少硬币的枚数。

输入

输入需要找给顾客的零钱n(单位:分)。

输出

输出所需最少硬币的枚数。

思路:可以用贪心做,也可以用动态规划做,这里使用的是动态规划,不难发现状态转移方程为dp[j]=min(dp[j-money[i]]+1,dp[j]),注意判断初始值情况。

money = [1, 5, 10, 25]
while True:
    try:
        n = int(input())
        dp = [0] * (n + 1)
        for i in range(4):
            for j in range(money[i], n + 1):
                dp[j] = min(dp[j - money[i]] + 1, dp[j]) if dp[j] != 0 else dp[j - money[i]] + 1
            # print(dp)
        print(dp[n])
    except:
        break

问题 F: 图书排序

题目描述

某图书销售管理系统需要对图书(Book)进行排序,每一本图书包含书名(bookName)、销量(bookSales)、价格(bookPrice)等属性,要求先按照销量由大到小排序,对于销量相同的图书再按照价格由小到大排序。

输入

每组输入包括两个部分,第一部分为书的数量n,
接下来n行则为n本书的信息。 按顺序输入书名(不超过20个字)、销量、价格。

输出

输出排序后的信息,每个属性用空格隔开

思路:简单的排序题,利用sort函数结合lambda表达式即可轻松解题。

while True:
    try:
        n, books = int(input()), []
        for _ in range(n):
            name, sales, price = input().split()
            books.append((name, sales, price))
        books.sort(key=lambda x: (-int(x[1]), float(x[2]), x[0]))
        for name, sales, price in books:
            print(name, sales, price)
    except:
        break

问题 G: 道具的魅力值

题目描述

在某网络游戏中提供了一个道具库,在道具库中每种道具均有若干件(数量已知),游戏玩家购买一件道具将获得一定的魅力值。
已知每种道具的价格和魅力值,请编写一个程序,在总价格不超过某个上限的情况下使得所购道具的魅力值之和达到最大。

输入

每组测试数据的输入有n+1行,n表示道具的种类。(n<=100,p<=10000)
第1行包含两个正整数,分别表示道具种类数n和总价值的上限p,两个数字之间用空格隔开。
第2行到第n+1行分别对应于第1种道具到第n种道具的信息,每1行包含三个正整数,两个数字之间用空格隔开,三个正整数分别表示某一种道具的数量、单个道具的价格和魅力值。

输出

每组测试数据的输出只有一行,即道具魅力值的最大和。

思路:多重背包的动态规划

while True:
    try:
        n, p = map(int, input().split())
        nums, prices, values, dp = [], [], [], [0] * (p + 1)
        for i in range(n):
            x, y, z = map(int, input().split())
            nums.append(x), prices.append(y), values.append(z)
        for index, price in enumerate(prices):
            for j in range(p, price - 1, -1):
                for k in range(min(nums[index], j // price) + 1):
                    dp[j] = max(dp[j], dp[j - k * price] + k * values[index])
        print(dp[p])
    except:
        break

问题 H: 团结军团

题目描述

一个篱笆三个桩,一个好汉三个帮!团结军团就是一支非常团结的军队。

团结军团团结的方式如下:

1、  团结军团排成N个纵队,每个纵队的士兵人数可以不同;

2、  每个士兵拥有一个基础武力值V(0 <= V <= 9);

3、  纵队中任意多个连续的士兵可以产生一个综合武力值,其值为这些连续士兵的基础武力值依序排列所构成的整数;

4、  军团中的综合武力值不重复产生。即任意的综合武力值最多仅记一次有效,重复的无效;

5、  计算综合武力值时可以去掉前导0,既前导0不影响实际的整型综合武力值;

6、  例如:对于拥有3个士兵的基础武力值分别为0、1、2的纵队可以产生的综合武力值有4个,分别为:0、1、2、12,其中01和012为重复产生的综合武力值无效。

对于这样一个团结的军团,你知道全军士兵的所有有效的综合武力值之和吗?

输入

第一行一个正整数N(1<=N<=1000),表示有N个纵队。

接下来有N行,每行代表一个纵队士兵基础武力值V(0 <= V <= 9)的排列,为一个由数字0-9构成的字符串S。

军团士兵人数不超过1000000。

输出

为一个整数,即全军士兵的有效综合武力值之和对1000000007取模的结果。

思路:不会,待更新。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为您讲解一下 HNUCM 最小生成树(Prim)的算法过程。 首先,Prim 算法是一种贪心算法,用于求解带权无向连通图的最小生成树。下面是具体的算法过程: 1. 选取任意一个顶点作为起点,将其加入到生成树中。 2. 找到与已经加入到生成树中的顶点相邻的所有顶点,并计算它们与生成树的距离。 3. 从这些顶点中选取距离最小的顶点,将其加入到生成树中。 4. 重复步骤2和3,直到生成树中包含了所有的顶点。 在具体实现时,可以使用一个数组来记录每个顶点是否已经加入到生成树中,以及另一个数组来记录每个顶点与生成树的距离。每次选择距离最小的顶点时,可以使用一个最小堆来优化。 至于 HNUCM 最小生成树 Prim 算法的具体实现,可以参考以下代码: ```python import heapq def prim(graph): n = len(graph) visited = [False] * n distance = [float('inf')] * n distance[0] = 0 heap = [(0, 0)] result = 0 while heap: (d, u) = heapq.heappop(heap) if visited[u]: continue visited[u] = True result += d for v, weight in graph[u]: if not visited[v] and weight < distance[v]: distance[v] = weight heapq.heappush(heap, (weight, v)) return result ``` 这段代码实现了 HNUCM 最小生成树 Prim 算法的过程,其中 graph 是一个邻接表表示的带权无向连通图,每个元素是一个二元组 (v, w),表示从节点 u 到节点 v 有一条边权为 w 的边。算法的返回值是最小生成树的总权值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值