CSDN每日一练 |『清理磁盘空间』『奇偶排序』『编号分组』2023-08-30
- 一、题目名称:清理磁盘空间
- 二、题目名称:奇偶排序
- 三、题目名称:奇偶排序
- 四、题目名称:编号分组
一、题目名称:清理磁盘空间
时间限制:1000ms内存限制:256M
题目描述:
小明电脑空间满了,决定清空间。为了简化问题,小明列了他个人文件夹(/data)中所有末级文件路径和大小,挑选出总大小为 m 的删除方案,求所有删除方案中,删除操作次数最小是多少。 一次删除操作:删除文件或者删除文件夹。如果删除文件夹,那么该文件夹包含的文件都将被删除。 文件夹的大小:文件夹中所有末级文件大小之和
输入描述:
第一行输入 n (n <= 1000)和 m(m <= 1000),表示文件数量,和需要删除的大小 接下去有 n 行,每一行都是一个文件绝对路径(路径长度小于 100),和这个文件的大小(小于 1000)
输出描述:
输出所有删除方案中,删除操作次数最小是多少。如果找不到恰好删除的大小为 m 的方案,则打印 -1
🚩 示例:
✔️ 示例1:
输入
6 10
/data/movie/a.mp4 5
/data/movie/b.mp4 3
/data/movie/c.mp4 2
/data/movie/d.mp4 4
/data/picture/a.jpg 4
/data/picture/b.jpg 1
输出
2
🔔 解题思路:
首先将输入的文件按照大小从小到大进行排序,然后使用动态规划来解决问题。
定义一个二维数组dp,dp[i][j]表示前i个文件中删除大小为j的文件夹或文件的最小操作次数。
逐个文件进行状态转移。对于第 i 个文件,有两种情况:
如果删除了第 i 个文件,那么最小操作次数为 dp[i-1][j-size_i] + 1,其中 size_i 表示第 i 个文件的大小。
如果不删除第 i 个文件,那么最小操作次数为 dp[i-1][j]。
取这两种情况中的较小值。
最终答案即为 dp[n][m],如果 dp[n][m] 为无穷大,则表示不存在满足条件的删除方案。
代码1如下:
def minOperation(n, m, files):
files.sort(key=lambda x: x[1]) # 按照文件大小排序
# 初始化dp数组
dp = [[float('inf')] * (m+1) for _ in range(n+1)]
dp[0][0] = 0
##dp[i][j] 表示前 i 个文件中,构成大小为 j 的文件夹所需的最小操作次数
## 对于每个文件,可以选择将其放入目标文件夹或不放入背包
for i in range(1, n+1):
size_i = files[i-1][1] # 第i个文件的大小
for j in range(m+1):
if j >= size_i:
# 如果当前背包容量大于等于文件大小,可以选择将文件放入背包或者不放入背包
dp[i][j] = min(dp[i-1][j-size_i] + 1, dp[i-1][j])
else:
# 当前背包容量小于文件大小,无法放入,只能选择不放入背包
dp[i][j] = dp[i-1][j]
if dp[n][m] == float('inf'):
return -1
else:
return dp[n][m] ##前 n 个文件构成大小为 m 的文件夹所需的最小操作次数
# 输入n和m
n, m = map(int, input().split())
files = []
# 输入每个文件的路径和大小
for _ in range(n):
path, size = input().split()
files.append((path, int(size)))
# 输出结果
print(minOperation(n, m, files))
代码2如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char path[100]; // 文件路径
int size; // 文件大小
} File;
int minOperation(int n, int m, File* files) {
int dp[m+1]; // 动态规划数组,dp[i] 表示大小为 i 的文件夹所需的最小操作次数
dp[0] = 0; // 大小为 0 的文件夹不需要操作
for (int i = 1; i <= m; ++i)
dp[i] = -1; // 初始化为 -1,表示无法达到该大小的文件夹
for (int i = 0; i < n; ++i) {
for (int j = m; j >= files[i].size; --j) {
// 如果可以从大小为 j-files[i].size 的文件夹转移过来
if (dp[j-files[i].size] != -1) {
// 如果当前大小为 j 的文件夹还未计算或转移次数更少,更新最小操作次数
if (dp[j] == -1 || dp[j-files[i].size] + 1 < dp[j]) {
dp[j] = dp[j-files[i].size] + 1;
}
}
}
}
return dp[m]; // 返回大小为 m 的文件夹所需的最小操作次数
}
int main() {
int n, m;
scanf("%d %d", &n, &m); // 输入文件夹数量和目标文件夹大小
File* files = (File*) malloc(n * sizeof(File)); // 动态分配文件数组内存
for (int i = 0; i < n; ++i)
scanf("%s %d", files[i].path, &(files[i].size)); // 输入每个文件的路径和大小
int result = minOperation(n, m, files); // 计算最小操作次数
printf("%d\n", result); // 输出结果
free(files); // 释放内存
return 0;
}
其他大佬解法:
###建树,然后树上背包###
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2010;
const int INF = 0x3f3f3f3f;
unordered_map<string, int> id;
vector<int> G[maxn];
int tot, n, m, w[maxn], dp[maxn][1010];
void dfs(int u)
{
memset(dp[u], 0x3f, sizeof(dp[u]));
dp[u][0] = 0;
sort(G[u].begin(), G[u].end());
G[u].erase(unique(G[u].begin(), G[u].end()), G[u].end());
for (int v : G[u])
{
dfs(v);
w[u] += w[v];
for (int i = min(m, w[u]); i >= 0; --i)
{
for (int j = min(i, w[v]); j; --j)
{
dp[u][i] = min(dp[u][i], dp[v][j] + dp[u][i - j]);
}
}
}
if (w[u] <= m)
dp[u][w[u]] = 1;
}
int main()
{
cin >> n >> m;
for (int i = 0, x; i < n; ++i)
{
string s;
cin >> s >> x;
s += "/";
int len = s.size(), p = 0;
string tmp = "";
for (int j = 1; j < len; ++j)
{
tmp += s[j];
if (s[j] == '/')
{
int now = id.count(tmp) ? id[tmp] : (id[tmp] = ++tot);
G[p].emplace_back(now);
p = now;
}
}
w[p] = x;
}
dfs(0);
if (dp[0][m] >= INF)
dp[0][m] = -1;
cout << dp[0][m] << endl;
return 0;
}
二、题目名称:奇偶排序
时间限制:1000ms内存限制:256M
题目描述:
一个数组里有奇数有偶数(乱序),调整数组顺序使奇数位于偶数前面。(测试用例仅做参考,我们会根据代码质量进行评分)
输入描述:
第一行输入整数n。 第二行输入n个整数。
输出描述:
输出排序后的n个整数。
🚩示例:
✔️示例1
输入
4
2 3 1 23
输出
3 1 23 2
🔔 解题思路:
🚩 奇偶排序问题有两个题目。。
代码1如下:
# 输入整数 n,表示数组的大小
n = int(input())
# 从标准输入读取以空格分隔的整数,并转换为列表 arr
arr = list(map(int, input().split()))
# 使用列表推导式筛选奇数和偶数
# odd 列表存储所有奇数
odd = [num for num in arr if num % 2 == 1]
# even 列表存储所有偶数
even = [num for num in arr if num % 2 == 0]
# 将奇数列表和偶数列表拼接成一个新的列表 result,其中奇数在前,偶数在后
result = odd + even
# 将列表 result 中的每个元素转换为字符串,并使用空格作为分隔符拼接起来,然后通过 print() 函数输出
print(" ".join(map(str, result))) # 或者使用 print(*result)
代码2如下:
# 输入n和数组元素
n = int(input())
nums = list(map(int, input().split()))
def oddEvenSort(nums):
# 分别构建奇数列表和偶数列表
odd_nums = []
even_nums = []
for num in nums:
if num % 2 == 0:
even_nums.append(num) # 偶数放入偶数列表
else:
odd_nums.append(num) # 奇数放入奇数列表
sorted_nums = odd_nums + even_nums # 将奇数列表和偶数列表合并
return sorted_nums
# 输出排序后的数组
sorted_nums = oddEvenSort(nums)
print(' '.join(map(str, sorted_nums)))
代码3如下:
#include <stdio.h>
#include <stdlib.h>
// 奇偶排序函数
void oddEvenSort(int nums[], int n) {
// 分别构建奇数列表和偶数列表
int *odd_nums = (int *)malloc(n * sizeof(int)); // 申请存储奇数的数组内存
int *even_nums = (int *)malloc(n * sizeof(int)); // 申请存储偶数的数组内存
int odd_count = 0; // 奇数的个数
int even_count = 0; // 偶数的个数
// 遍历原始数组,将奇数放入奇数列表,偶数放入偶数列表
for (int i = 0; i < n; i++) {
if (nums[i] % 2 == 0) {
even_nums[even_count++] = nums[i]; // 偶数放入偶数列表
} else {
odd_nums[odd_count++] = nums[i]; // 奇数放入奇数列表
}
}
// 将奇数列表和偶数列表合并到原始数组
for (int i = 0; i < odd_count; i++) {
nums[i] = odd_nums[i];
}
for (int i = 0; i < even_count; i++) {
nums[i + odd_count] = even_nums[i];
}
free(odd_nums); // 释放奇数列表内存
free(even_nums); // 释放偶数列表内存
}
int main() {
int n;
scanf("%d", &n); // 输入元素个数
int *nums = (int *)malloc(n * sizeof(int)); // 申请存储原始数组的内存
for (int i = 0; i < n; i++) {
scanf("%d", &nums[i]); // 输入数组元素
}
oddEvenSort(nums, n); // 调用奇偶排序函数进行排序
// 输出排序后的数组
for (int i = 0; i < n; i++) {
printf("%d ", nums[i]);
}
printf("\n");
free(nums); // 释放原始数组内存
return 0;
}
代码4如下:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt(); // 输入元素个数
int[] nums = new int[n]; // 原始数组
for (int i = 0; i < n; i++) {
nums[i] = scanner.nextInt(); // 输入数组元素
}
int[] sorted_nums = oddEvenSort(nums); // 调用奇偶排序函数进行排序
for (int i = 0; i < n; i++) {
System.out.print(sorted_nums[i] + " "); // 输出排序后的数组
}
}
/**
* 将奇数和偶数分开并进行排序
* @param nums 原始数组
* @return 排序后的数组
*/
public static int[] oddEvenSort(int[] nums) {
int[] odd_nums = new int[nums.length]; // 存储奇数的数组
int[] even_nums = new int[nums.length]; // 存储偶数的数组
int odd_index = 0; // 奇数数组的索引
int even_index = 0; // 偶数数组的索引
for (int i = 0; i < nums.length; i++) {
if (nums[i] % 2 == 0) {
even_nums[even_index++] = nums[i]; // 偶数放入偶数数组
} else {
odd_nums[odd_index++] = nums[i]; // 奇数放入奇数数组
}
}
int[] sorted_nums = new int[nums.length]; // 存储排序后的数组
System.arraycopy(odd_nums, 0, sorted_nums, 0, odd_index); // 复制奇数数组到排序数组
System.arraycopy(even_nums, 0, sorted_nums, odd_index, even_index); // 复制偶数数组到排序数组
return sorted_nums;
}
}
三、题目名称:奇偶排序
时间限制:1000ms内存限制:256M
题目描述:
给定一个存放整数的数组,重新排列数组使得数组左边为奇数,右边为偶数。(测试用例仅做参考,我们会根据代码质量进行评分)
输入描述:
第一行输入整数n。(1<=n<=1000)表示数组大小 第二行输入n个整数a.(1<=n<=100)
输出描述:
输出重排之后的数组。
🚩示例:
✔️示例1
输入
6
3 34 67 89 90 58
输出
3 67 89 34 90 58
🔔 解题思路:
奇数按从小到大的顺序排列并输出,偶数则保持输入顺序输出。
代码1如下:
# 输入整数 n,表示数组的大小
n = int(input())
# 从标准输入读取以空格分隔的整数,并转换为列表 arr
arr = list(map(int, input().split()))
# 使用列表推导式筛选奇数和偶数
# odd 列表存储所有奇数
odd = [num for num in arr if num % 2 == 1]
# even 列表存储所有偶数
even = [num for num in arr if num % 2 == 0]
# 将奇数列表和偶数列表拼接成一个新的列表 result,其中奇数在前,偶数在后
result = odd + even
# 将列表 result 中的每个元素转换为字符串,并使用空格作为分隔符拼接起来,然后通过 print() 函数输出
print(" ".join(map(str, result))) # 或者使用 print(*result)
代码2如下:
# 输入n和数组元素
n = int(input())
nums = list(map(int, input().split()))
def oddEvenSort(nums):
# 分别构建奇数列表和偶数列表
odd_nums = []
even_nums = []
for num in nums:
if num % 2 == 0:
even_nums.append(num) # 偶数放入偶数列表
else:
odd_nums.append(num) # 奇数放入奇数列表
sorted_nums = odd_nums + even_nums # 将奇数列表和偶数列表合并
return sorted_nums
# 输出排序后的数组
sorted_nums = oddEvenSort(nums)
print(' '.join(map(str, sorted_nums)))
代码3如下:
#include <stdio.h>
#include <stdlib.h>
// 奇偶排序函数
void oddEvenSort(int nums[], int n) {
// 分别构建奇数列表和偶数列表
int *odd_nums = (int *)malloc(n * sizeof(int)); // 申请存储奇数的数组内存
int *even_nums = (int *)malloc(n * sizeof(int)); // 申请存储偶数的数组内存
int odd_count = 0; // 奇数的个数
int even_count = 0; // 偶数的个数
// 遍历原始数组,将奇数放入奇数列表,偶数放入偶数列表
for (int i = 0; i < n; i++) {
if (nums[i] % 2 == 0) {
even_nums[even_count++] = nums[i]; // 偶数放入偶数列表
} else {
odd_nums[odd_count++] = nums[i]; // 奇数放入奇数列表
}
}
// 将奇数列表和偶数列表合并到原始数组
for (int i = 0; i < odd_count; i++) {
nums[i] = odd_nums[i];
}
for (int i = 0; i < even_count; i++) {
nums[i + odd_count] = even_nums[i];
}
free(odd_nums); // 释放奇数列表内存
free(even_nums); // 释放偶数列表内存
}
int main() {
int n;
scanf("%d", &n); // 输入元素个数
int *nums = (int *)malloc(n * sizeof(int)); // 申请存储原始数组的内存
for (int i = 0; i < n; i++) {
scanf("%d", &nums[i]); // 输入数组元素
}
oddEvenSort(nums, n); // 调用奇偶排序函数进行排序
// 输出排序后的数组
for (int i = 0; i < n; i++) {
printf("%d ", nums[i]);
}
printf("\n");
free(nums); // 释放原始数组内存
return 0;
}
代码4如下:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt(); // 输入元素个数
int[] nums = new int[n]; // 原始数组
for (int i = 0; i < n; i++) {
nums[i] = scanner.nextInt(); // 输入数组元素
}
int[] sorted_nums = oddEvenSort(nums); // 调用奇偶排序函数进行排序
for (int i = 0; i < n; i++) {
System.out.print(sorted_nums[i] + " "); // 输出排序后的数组
}
}
/**
* 将奇数和偶数分开并进行排序
* @param nums 原始数组
* @return 排序后的数组
*/
public static int[] oddEvenSort(int[] nums) {
int[] odd_nums = new int[nums.length]; // 存储奇数的数组
int[] even_nums = new int[nums.length]; // 存储偶数的数组
int odd_index = 0; // 奇数数组的索引
int even_index = 0; // 偶数数组的索引
for (int i = 0; i < nums.length; i++) {
if (nums[i] % 2 == 0) {
even_nums[even_index++] = nums[i]; // 偶数放入偶数数组
} else {
odd_nums[odd_index++] = nums[i]; // 奇数放入奇数数组
}
}
int[] sorted_nums = new int[nums.length]; // 存储排序后的数组
System.arraycopy(odd_nums, 0, sorted_nums, 0, odd_index); // 复制奇数数组到排序数组
System.arraycopy(even_nums, 0, sorted_nums, odd_index, even_index); // 复制偶数数组到排序数组
return sorted_nums;
}
}
四、题目名称:编号分组
时间限制:1000ms内存限制:256M
题目描述:
现欲对各有正整数编号的n个人进行分组,规定同组中不得出现两人编号乘积之开立方为正整数的情况,求构成最大分组的人数。
输入描述:
单行n个数字表示各人的编号(0<n<100000,每个数字的范围在1到2000000000之间)。
输出描述:
构成最大分组的人数d(d>=1)
🚩示例:
✔️示例1
输入
27 4 2 16
输出
3
🔔 提示:
仅限于两人编号乘积,而非一人本身的编号,或者是三人或更多人的编号乘积
🔔 解题思路:
首先,我们需要判断两个人的编号乘积的开立方是否为正整数。如果是正整数,则说明两个人不允许在同一组中。
其次,我们可以使用哈希表来记录每个人的编号及其出现的次数。然后遍历哈希表,对于每个编号,计算其开立方并判断是否为整数,如果是整数,则该编号不能与其他编号在同一组中。
最后,统计剩余可分配到同一组的人数,即为最大分组的人数。
代码1如下:
使用集合和列表来实现,遍历每对编号,计算其乘积,并判断乘积的立方根是否为整数。如果是整数,则将两个编号都加入一个集合invalid_nums,表示它们不满足条件。
最后,通过计算总人数减去不满足条件的编号数,即可得到最大分组的人数。
# 读取输入
nums = list(map(int, input().split()))
# 使用集合存储不满足条件的编号
invalid_nums = set()
# 遍历每对编号,判断其乘积的立方根是否为整数
for i in range(len(nums)):
for j in range(i+1, len(nums)):
product = nums[i] * nums[j]
if int(product ** (1/3)) ** 3 == product:
invalid_nums.add(nums[i]) # 将第一个编号加入不满足条件的集合
invalid_nums.add(nums[j]) # 将第二个编号加入不满足条件的集合
# 计算最大分组的人数
maxGroup = len(nums) - len(invalid_nums)
# 输出结果
print(maxGroup)
代码2如下:
import math
# 读取输入
nums = list(map(int, input().split()))
# 定义函数:获取最大分组的人数
def getMaxGroup(nums):
counts = {} # 创建一个空的字典,用于记录每个编号出现的次数
for num in nums:
if num in counts: # 如果当前编号已经在字典中,则将其出现次数加1
counts[num] += 1
else:
counts[num] = 1 # 否则将其加入字典,并设置出现次数为1
maxGroup = 0 # 初始化最大分组人数为0
for num in counts:
isValid = True # 标记当前编号是否与其他编号乘积的立方根为整数
for otherNum in counts:
if num == otherNum: # 如果是同一个编号,则跳过比较
continue
if math.isqrt(num * otherNum) ** 3 == num * otherNum: # 判断两个编号乘积的立方根是否为整数
isValid = False # 如果是整数,则将标记置为False,并跳出循环
break
if isValid: # 如果当前编号不与任何其他编号满足条件,则将其出现次数累加到最大分组人数上
maxGroup += counts[num]
return maxGroup # 返回最大分组人数
# 计算最大分组的人数
maxGroup = getMaxGroup(nums)
# 输出结果
print(maxGroup)
代码3如下:
# 读取输入
n = input()
def max_group(n):
nums = n.split() # 将输入的数字分割成列表
groups = {} # 存储分组情况
for num in nums:
num = int(num)
for group in groups.values():
flag = True
for member in group:
if (num * member) ** (1/3) == int((num * member) ** (1/3)):
flag = False
break
if not flag:
continue
group.append(num)
break
else:
groups[len(groups)] = [num] # 加入新的分组
max_group_size = max(len(group) for group in groups.values()) # 统计最大分组人数
return max_group_size
result = max_group(n)
print(result)
代码4如下:
#include <stdio.h>
#include <math.h>
#define MAX_SIZE 100
int getMaxGroup(int nums[], int n) {
int counts[MAX_SIZE] = {0};
int maxGroup = 0;
// 统计每个编号的出现次数
for (int i = 0; i < n; i++) {
counts[nums[i]]++;
}
for (int i = 0; i < n; i++) {
int isValid = 1;
// 判断当前编号是否与其他编号乘积的立方根为整数
for (int j = 0; j < n; j++) {
if (i == j) {
continue;
}
if (pow(nums[i] * nums[j], 1.0 / 3) - (int)(pow(nums[i] * nums[j], 1.0 / 3)) == 0) {
isValid = 0;
break;
}
}
if (isValid) {
maxGroup += counts[nums[i]];
}
}
return maxGroup;
}
int main() {
int nums[MAX_SIZE];
int n;
// 读取输入
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &nums[i]);
}
// 计算最大分组的人数
int maxGroup = getMaxGroup(nums, n);
// 输出结果
printf("%d\n", maxGroup);
return 0;
}