dm

1.1 半数集问题

问题描述:
给定一个自然数 n,由 n 开始可以依次产生半数集 set(n)中的数如下。
(1) n∈set(n);
(2) 在 n 的左边加上一个自然数,但该自然数不能超过最近添加的数的一半;
(3) 按此规则进行处理,直到不能再添加自然数为止。
例如,set(6)={6,16,26,126,36,136}。半数集 set(6)中有 6 个元素。
注意半数集是多重集。
编程任务:
对于给定的自然数 n,编程计算半数集 set(n)中的元素个数。
数据输入:
每个输入包含若干行,每行给出一个整数 n。(0<n<1000) 直到0结束输入。
结果输出:
输出中每行输出半数集 set(n)中的元素个数。
样例输入:
1
2
6
0
样例输出:
1
2
6
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1010; // 常量:最大值

int n; // 自然数n
int a[N]; // 数组a,存储已经处理过的数的结果,用于避免重复计算

int f(int x) // 递归函数,计算半数集 set(n)中的元素个数。参数 x 表示当前计算的数
{
int res = 1; // 初始化结果为1,因为该数本身就在半数集中
for (int i = 1; i <= x / 2; i ++) // 遍历所有可能添加到该数左边的自然数
{
if (a[i] != 0) res += a[i]; // 如果该自然数已经被处理过,则直接加上其结果
else
{
a[i] = f(i); // 否则递归计算该自然数所在半数集的元素个数
res += a[i]; // 并将结果加到当前结果中
}
}

return res; // 返回计算结果
}

int main()
{
while(scanf("%d",&n) != EOF && n!=0) cout << f(n) << endl; // 多组数据输入,读入到n中,并调用f函数计算输出结果
return 0;
}
1.2 双色 Hanoi 塔问题
问题描述:
设 A、B、C 是 3 个塔座。开始时,在塔座 A 上有一叠共 n 个圆盘,这些圆盘自下而上,由大到小地叠在一起。各圆盘从小到大编号为 1,2,……,n,奇数号圆盘着蓝色,偶数号圆盘着红色,如图所示。现要求将塔座 A 上的这一叠圆盘移到塔座 B 上,并仍按同样顺序叠置。在移动圆盘时应遵守以下移动规则:
规则(1):每次只能移动 1 个圆盘;
规则(2):任何时刻都不允许将较大的圆盘压在较小的圆盘之上;
规则(3):任何时刻都不允许将同色圆盘叠在一起;
规则(4):在满足移动规则(1)-(3)的前提下,可将圆盘移至 A,B,C 中任一塔座上。
试设计一个算法,用最少的移动次数将塔座 A 上的 n 个圆盘移到塔座 B 上,并仍按同样顺序叠置。
编程任务:
对于给定的正整数 n,编程计算最优移动方案。
数据输入:
给定的正整数 n。
结果输出:
每一行由一个正整数 k 和 2 个字符 c1 和 c2 组成,表示将第 k 个圆盘从塔座 c1 移到塔座 c2 上。
样例输入
3
样例输出
1 A B
2 A C
1 B C
3 A B
1 C A
2 C B
1 A B

#include <iostream>

using namespace std;

int n;

// 移动圆盘函数
void moveDisk(int k, char c1, char c2)
{
cout << k << ' ' << c1 << ' ' << c2 << endl; // 输出移动的圆盘编号和起始塔座、目标塔座
}

// 汉诺塔递归函数
void hanoi(int n, char a, char b, char c)
{
if (n == 1) // 如果只有一个圆盘,直接将其从a塔座移动到b塔座
{
moveDisk(n, a, b);
return;
}

hanoi(n-1, a, c, b); // 将n-1个圆盘从a塔座经过b塔座移动到c塔座
moveDisk(n, a, b); // 将第n个圆盘从a塔座移动到b塔座
hanoi(n-1, c, b, a); // 将n-1个圆盘从c塔座经过a塔座移动到b塔座
}

int main()
{
cin >> n; // 输入圆盘个数
hanoi(n, 'A', 'B', 'C'); // A、B、C代表三根柱子,调用汉诺塔函数

return 0;
}

解释:
1. `moveDisk`函数用于输出移动的圆盘编号和起始塔座、目标塔座。
2. `hanoi`函数是一个递归函数,用于实现汉诺塔的移动操作。
3. 当只有一个圆盘时,直接将其从起始塔座移动到目标塔座。
4. 当有多个圆盘时,先将n-1个圆盘从起始塔座经过目标塔座移动到辅助塔座,然后将第n个圆盘从起始塔座移动到目标塔座,最后将n-1个圆盘从辅助塔座经过起始塔座移动到目标塔座。
5. 在程序主函数中,输入圆盘个数n,并调用汉诺塔函数将圆盘从起始塔座A移动到目标塔座B。

1.3 整数因子分解问题
问题描述:
大于 1 的正整数 n 可以分解为:n=x1x2…*xm。 例如,当 n=12 时,共有 8 种不同的分解式: 12=12;12=6∗2;12=4∗3;12=3∗4;12=3∗2∗2;12=2∗6;12=2∗3∗2;12=2∗2∗3。
编程任务:
对于给定的正整数 n,编程计算 n 共有多少种不同的分解式。
数据输入:
输入数据第一行有 1 个正整数 n (1≤n≤2000000000)。
结果输出:
输出不同的分解式数。
样例输入
12
样例输出
8

#include <algorithm>
#include <cstring>
#include <iostream>

using namespace std;

const int N = 2e8; // 常量N表示数组a的大小上限

int n;
int a[N]; // 记忆化数组,用于保存计算结果

int f(int n) // 记忆化搜索,公式:f(n) = sum(f(i)) 2 <= i <= n && n % i == 0
{
if(n < N && a[n] != 0) // 如果n值较小且已经计算过,直接返回保存的结果避免重复计算
return a[n];

int sum = 1, i = 2; // sum = 1表示 1 * n
for( ; i * i < n; i ++) // 遍历从2到sqrt(n)的因子i
if(n % i == 0) sum += f(i) + f(n / i); // 如果n能被i整除,则计算f(i)和f(n/i)并加到sum中
if(i * i == n) sum += f(i); // 如果n是完全平方数,则只计算一次f(i)
if(n < N) a[n] = sum; // 将计算结果保存到记忆化数组中

return sum; // 返回分解式总数
}

int main()
{
cin >> n; // 输入正整数n
cout << f(n) << endl; // 输出n的不同分解式总数

return 0;
}

解释:
1. `a`是一个大小为N的记忆化数组,用于保存计算结果以避免重复计算。
2. `f`函数使用记忆化搜索的方式计算正整数n的不同分解式总数。
3. 如果n值较小且已经计算过,直接返回保存的结果,避免重复计算。
4. 初始时,sum=1表示只有一种分解式:1 * n。
5. 在for循环中,遍历从2到sqrt(n)的因子i。
6. 如果n能被i整除,说明存在一种分解式为n = i * (n / i),则递归地计算f(i)和f(n / i),并将结果累加到sum中。
7. 如果n是完全平方数,即i * i == n,只计算一次f(i)。
8. 将计算结果sum保存到记忆化数组a中。
9. 最后返回分解式总数。

2.1 我要去西藏
问题描述:
雪域高原,美丽的天路,是许多人向往已久的旅游胜地,更是自驾游爱好者必须前往的目的地。
假设从济南到西藏的路线上共有n个城市1,2,…,n,每个城市都有一个租车公司。你可以在这些城市出租汽车,并在之后的任何一个城市归还汽车。城市i到城市j之间的租金为x(i,j),1≤i<j≤n。试设计一个算法,计算从济南(城市1)到西藏(城市n)所需的最少租金。
编程任务:
对于给定的城市 i 到城市 j 之间的租金为 x(i,j),1≤i<j≤n,编程计算从城市 1 到城市 n 所需的最少租金。
数据输入:
第1行为n,n<=200),表示有 n个城市。接下来的 n-1 行是 x(i,j),1≤i<j≤n。
结果输出:
计算出的从城市1到城市n所需的最少租金。
样例输入:
3
1 3
5
样例输出:
3
说明
1 3表示: 从城市1到城市2的租金为1 从城市1到城市3的租金为3

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 210; // 城市数量的上限

int n;
int w[N][N], f[N][N]; // w表示城市之间的租金,f表示从1到当前城市的最少租金

int main()
{
memset(w, 1e8 + 10, sizeof w); // 将w数组初始化为一个较大的值,表示两个城市之间没有直接的租车路线
cin >> n; // 输入城市数量
for (int i = 1; i < n; i ++) // 输入每对城市之间的租金
for (int j = i + 1; j <= n; j ++)
cin >> w[i][j];

for (int i = 1; i <= n; i ++) // 枚举从1到当前城市i的最少租金
for (int j = i + 1; j <= n; j ++) // 枚举从i到当前城市j的最少租金
{
f[i][j] = 1e8; // 初始时将最少租金设为一个较大的值
for (int k = 1; k <= n; k ++) // 枚举从i到j中的中转城市k
f[i][j] = min(f[i][j], f[i][k] + w[k][j]); // 更新从i到j的最少租金
}
if (f[1][n] != 1e8) cout << f[1][n] << endl; // 输出从1到n的最少租金,如果为1e8则表示无法到达n

return 0;
}


2.2 云天明的项链
问题描述:
云天明送给程心一串珍贵的项链,上面共有n颗珍珠,每一颗珍珠上都有一个数字。
每两颗相邻的珍珠可以合并为一颗新的珍珠,合并后这两颗珍珠消失,新珍珠上的数字为合并的两颗的的数字之和。并且此次操作的得分要加上这个和。
经过n-1次这样的合并后,项链只剩下最后一颗珍珠,问总得分的最小值和最大值。
编程任务:
对于给定n颗珍珠组成的项链,编程计算合并成一颗珍珠时的最小得分和最大得分。
数据输入:
输入的第 1 行是正整数n,1≤n≤100,表示有 n 颗珍珠。 第二行有n个数,分别表示每颗珍珠上的数字。
结果输出:
输出的第 1 行中的数是最小得分;第 2 行中的数是最大得分。
样例输入:
3
1 2 3
样例输出:
9 11
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 310;

int n;
int f[2 * N][2 * N], g[2 * N][2 * N]; // 状态表示
int s[2 * N]; // 前缀和数组

int main()
{
cin >> n; // 输入珍珠的数量
for (int i = 1; i <= n; i ++)
{
cin >> s[i]; // 输入每颗珍珠上的数字
s[n + i] = s[i]; // 将珍珠序列进行扩展,以便模拟项链环状连接
}
for (int i = 1; i <= 2 * n; i ++) s[i] += s[i - 1]; // 计算前缀和,用于快速计算区间内的数字和

for (int len = 2; len <= n; len ++) // 枚举所有合并后的长度,从2开始,长度为1的石子堆不需要合并,也没有合并代价
for (int i = 1; i + len - 1 <= 2 * n; i ++) // 枚举每次长度的起点,从小到大顺序计算
{
int l = i, r = i + len - 1; // 计算合并区间的起点l和终点r
f[l][r] = 1e9; // 因为f[N][N]初始化为全0,且要取min,所以先赋大值,否则结果全为0
for (int k = l; k < r; k ++) // 合并最后一步的所有分类,取最小值
{
f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r] + s[r] - s[l - 1]); // 计算合并区间[l,r]的最小得分
g[l][r] = max(g[l][r], g[l][k] + g[k + 1][r] + s[r] - s[l - 1]); // 计算合并区间[l,r]的最大得分
}
}

int Min = 1e9, Max = 0; // 初始化最小得分和最大得分
for (int i = 1; i <= n; i ++)
{
Min = min(Min, f[i][n + i - 1]); // 更新最小得分
Max = max(Max, g[i][n + i - 1]); // 更新最大得分
}
cout << Min << endl; // 输出最小得分
cout << Max << endl; // 输出最大得分

return 0;
}
2.3 扩展距离
问题描述:
对于长度相同的 2 个字符串 A 和 B,其距离定义为相应位置字符距离之和。2 个非空格字符的距离是它们的 ASCII 码之差的绝对值。空格与空格的距离为 0;空格与其它字符的距离为一定值 k。
在一般情况下,字符串 A 和 B 的长度不一定相同。字符串 A 的扩展是在 A 中插入若干空格字符所产生的字符串。在字符串 A 和 B 的所有长度相同的扩展中,有一对距离最小的扩展,该距离称为字符串 A 和 B 的扩展距离。
对于给定的字符串 A 和 B,试设计一个算法,计算其扩展距离。
编程任务:
对于给定的字符串 A 和 B,编程计算其扩展距离。
数据输入:
输入数据第 1 行是字符串 A;第 2 行是字符串 B。第 3 行是空格与其它字符的距离定值 k。
结果输出:
输出计算出的字符串 A 和 B 的扩展距离。
样例输入:
cmc
snmn
2
样例输出:
10
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 10010;

char a[N], b[N]; // 定义两个字符串a和b
int k; // 定义空格与其它字符的距离k
int f[N][N]; // 定义状态数组f

int main()
{
cin >> a >> b >> k; // 输入两个字符串a和b,以及空格与其它字符的距离k
int la = strlen(a), lb = strlen(b); // 获取字符串a和b的长度
for (int i = la; i >= 1; i --) // 在字符串a中插入空格
{
a[i] = a[i - 1]; // 向后移动一位
f[i][0] = k * i; // 计算状态f[i][0],即将a[i]与空格对齐时的距离
}
for (int i = lb; i >= 1; i --) // 在字符串b中插入空格
{
b[i] = b[i - 1]; // 向后移动一位
f[0][i] = k * i; // 计算状态f[0][i],即将b[i]与空格对齐时的距离
}

for (int i = 1; i <= la; i ++) // 枚举字符串a的每个字符
for (int j = 1; j <= lb; j ++) // 枚举字符串b的每个字符
{
f[i][j] = min(min(f[i - 1][j] + k, f[i][j - 1] + k), f[i - 1][j - 1] + abs(a[i] - b[j])); // 计算状态f[i][j],即将a[i]与b[j]对齐时的距离。根据题意,有三种情况,取最小值
}
cout << f[la][lb] << endl; // 输出扩展距离

return 0;
}
2.4 双胞胎探宝
问题描述:
现有一对双胞胎,在一个有n * n个方格的方形宝藏区域F中探宝。(i,j)方格中宝物的价值为v(i,j),如下图所示。
A
3 2
5
7 1
9 B
双胞胎均从F的A点出发,向下或向右行走,直到B点,在走过的路上,收集方格中的宝藏。试找出兄弟二人可以获得的宝藏总价的值最大。
数据输入:
输入数据第1行有1个正整数n,表示方形区域F有n * n个方格。
接下来每行有3个整数,前2个表示方格位置,第3个数为该位置宝藏价值。最后一行是3个0。
样例输入:
8
2 2 3
2 5 2
3 4 5
4 1 7
4 3 1
5 2 9
0 0 0
样例输出:
24
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 110;

int n;
int w[N][N]; // 存储每个方格中宝物的价值
int f[N][N][N][N]; // 动态规划状态数组,f[i][j][a][b]表示兄弟在(i, j)和(a, b)位置时的最大宝藏总价值

int main()
{
cin >> n; // 输入n的值,即方形宝藏区域的大小
int a, b, c;
while (cin >> a >> b >> c)
{
if (!a && !b && !c) break; // 输入0 0 0表示宝藏输入结束
w[a][b] = c; // 记录宝藏的位置和价值
}
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= n; j ++)
for (a = 1; a <= n; a ++)
for (b = 1; b <= n; b ++)
{
int sum = w[i][j] + w[a][b]; // 计算当前位置的宝藏总价值
if (i == a && j == b) sum = w[i][j]; // 如果两个兄弟在同一个位置,只计算一次该位置的宝藏价值
f[i][j][a][b] = max(max(f[i - 1][j][a - 1][b], f[i - 1][j][a][b - 1]),
max(f[i][j - 1][a - 1][b], f[i][j - 1][a][b - 1])) + sum; // 动态规划状态转移方程,取四种情况下的最大值作为当前状态的最大值
}
cout << f[n][n][n][n] << endl; // 输出兄弟可以获得的宝藏总价值

return 0;
}
2.5 双胞胎做题
问题描述:
有兄弟二人做n道题目。
已知哥哥做题目i需要时间ai,弟弟做题目i需要时间bi 。
由于兄弟二人对知识掌握的程度不同,很可能对于某些 i,有 ai ≥ bi ,而对于某些 j,j≠i,有 aj < bj 。
每一道题目只能交给二人中的一个来做。
设计一个算法,使得二人解掉所有题目所用的时间最短(从任何一人开始做题到最后一人解掉最后一道题目的总时间)。
研究一个实例: (a1,a2,a3,a4,a5,a6)=(2,5,7,10,5,2);(b1,b2,b3,b4,b5,b6)=(3,8,4,11,3,4)。
数据输入:
输入的第 1 行是 1 个正整数 n, 表示要处理 n 个作业。
接下来的 2 行中,每行有 n 个正整数,ai和bi。
结果输出:
输出计算出的最短解题时间。
样例输入:
6
2 5 7 10 5 2
3 8 4 11 3 4
样例输出:
15

#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10;
int n;
int ta=0;
int mint=0x3f3f3f;
int a[N];
int b[N];
int F[N][N];

int solve()
{
memset(F, 0, sizeof(F));
for(int i=1;i<=n;i++)
{
for(int j=0;j<=ta;j++)
{
if(j<a[i])
F[i][j]=F[i-1][j]+b[i];
else
{
F[i][j]=min(F[i-1][j-a[i]],F[i-1][j]+b[i]);
}
}
}
for(int j=0;j<=ta;j++)
{
int t=max(F[n][j],j);
if(mint>t)
mint=t;
}
return mint;
}

int main()
{
cin >> n;
for(int i=1;i<=n;i++)
{
cin >> a[i];
ta=ta+a[i];
}
for(int i=1;i<=n;i++)
{
cin >> b[i];
}
solve();
cout << mint;
}
2.6 少打比赛多训练
问题描述:
作为一个比赛队伍总教练的你,请解决下面的问题:
你作为总教练的聘期为n天。
在这n天里,一共有n个比赛可以参加。
队员们都愿意参加比赛,而不愿意训练,所以希望有比赛就参加。
如果在某一天有多场比赛同时开始,作为主教练的你可任选其中一场比赛参加(前提是队伍没有正在参加的比赛)。
比赛从第s天开始,持续t天,则该比赛在第 s+t-1 天结束。
作为总教练的你,应该如何选择比赛,才能既使得队员满意(有比赛就参加),又能尽可能延长训练的时间,减少参加比赛的时间。
编程任务:
对于给定的比赛时间表,编程计算最少的比赛天数。
数据输入:
第 1 行有 2 个正整数 n 和 k。n 表示总天数;k 是比赛个数。接下来的 k 行中,每行有 2 个表示比赛的整数 s 和 t,该比赛从第 s 天开始,持续 t 天。
0 < n, k < 10000
样例输入:
15 6
1 2
1 6
4 11
8 5
8 1
11 5
样例输出
11
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

ll dp[100002]; // dp数组存储最小比赛天数
ll n, m; // 总天数n,比赛个数m
ll s[100002] = {0}; // s数组存储每天的比赛个数
map<ll, int> T; // T映射存储比赛开始时间和持续时间

struct node {
ll u; // 比赛开始时间
ll t; // 比赛持续时间
} a[100002];

int main() {
cin >> n >> m;
for (int i = 0; i < m; i++) {
scanf("%lld%lld", &a[i].u, &a[i].t);
T[a[i].u * 100000 + (s[a[i].u])++] = a[i].t; // 将比赛时间信息存入T映射中
}
memset(dp, 0x3f, sizeof(dp));
dp[n + 1] = 0;
for (int i = n; i >= 1; i--) { // 从第n天开始向前遍历
if (s[i] == 0) {
dp[i] = dp[i + 1]; // 如果第i天没有比赛,则第i天的最小比赛天数等于第i+1天的最小比赛天数
} else {
for (int j = 0; j < s[i]; j++) {
dp[i] = min(dp[i + T[i * 100000 + j]] + T[i * 100000 + j], dp[i]); // 更新第i天的最小比赛天数
}
}
}
cout << dp[1] << endl; // 输出最小比赛天数
return 0;
}

3.1 教室安排问题
问题描述:
山东建筑大学有足够多的教室,可以安排一学期内的所有课程。当然,教务处希望使用的教室数量越少越好。设计一个有效的贪心算法进行排课。
编程任务:
对于给出的 n 个待排课的课程,编程计算使用最少教室的数量。0 ≤ n ≤ 10000。
数据输入:
输入数据第一行有 1 个正整数 n,表示有 n 个待排课的课程。
接下来的 n 行中,每行有 2 个正整数,分别表示 n 个待排课程的开始时间和结束时间。时间以 0 点开始的分钟计。
结果输出:
输出需要的最少教室数量。
样例输入:
5
1 10
5 28
9 35
28 80
36 50
样例输出:
3
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 10010;

// 定义结构体Cour表示课程的开始时间和结束时间
struct Cour {
int start_time;
int end_time;

// 重载小于运算符,用于排序
bool operator<(const Cour& other) const {
return start_time < other.start_time;
}
} cour[N];

int n;
int end_times[N]; // 记录每个教室当前存放的课程的结束时间

int main() {
// 输入待排课程数量n
cin >> n;

// 输入每个待排课程的开始时间和结束时间
for (int i = 0; i < n; i++) {
int start, end;
cin >> start >> end;
cour[i] = {start, end};
}

// 按照课程的开始时间升序排序,确保每次选择开始时间最早的课程
sort(cour, cour + n);

int rooms_needed = 1; // 记录最终所需的教室数量

// 初始化第一个教室的结束时间为第一门课的结束时间
end_times[1] = cour[0].end_time;

for (int i = 1; i < n; i++) {
bool assigned = false;

// 遍历当前已有的教室,找到合适的教室分配当前课程
for (int j = 1; j <= rooms_needed; j++) {
if (cour[i].start_time >= end_times[j]) {
// 更新教室的结束时间
end_times[j] = cour[i].end_time;
assigned = true;
break;
}
}

// 如果无法找到合适的教室,则新增一个教室
if (!assigned) {
end_times[++rooms_needed] = cour[i].end_time;
}
}

// 输出最少教室数量
cout << rooms_needed << endl;

return 0;
}
3.2 磁带最优存储问题
问题描述:
设有 n 个程序{1,2,…, n }要存放在长度为 L 的磁带上。程序 i 存放在磁带上的长度是li ,1 ≤ i ≤ n 。这 n 个程序的读取概率分别是p1, p2 ,……, pn ,且 ∑pi(i=1~n) = 1。
如果将这 n 个程序按i1,i2 ,……,in 的次序存放,则读取程序ir 所需的时间tr = c*∑pi li (i=1 ~ r)。这 n 个程序的平均读取时间为∑tr (r=1 ~ n)。
磁带最优存储问题要求确定这 n 个程序在磁带上的一个存储次序,使平均读取时间达到最小。试设计一个解此问题的算法,并分析算法的正确性和计算复杂性。
编程任务:
对于给定的 n 个程序存放在磁带上的长度和读取概率,编程计算 n 个程序的最优存储方案。
数据输入:
输入数据第一行是正整数 n,表示文件个数。接下来的 n 行中,每行有 2 个正整数 a 和 b,分别表示程序存放在磁带上的长度和读取概率。实际上,第 k 个程序的读取概率 ak / ∑ai (i = 1 ~ n)。对所有输入均假定 c=1。
0 < n ≤ 10000
结果输出:
将编程计算出的最小平均读取时间输出。
样例输入:
5
71 872
46 452
9 265
73 120
35 85
样例输出:
85.6193
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 10005;

// 定义程序结构体Pro,包含程序的长度a、读取概率b、以及计算得到的权重p
struct Pro {
int length;
int probability;
double weight;

// 重载小于运算符,用于排序
bool operator<(const Pro& other) const {
return weight < other.weight;
}
} pro[N];

int n;

int main() {
// 输入程序数量n
cin >> n;
int length, probability, sum = 0;

// 输入每个程序的长度和读取概率,并计算读取概率的总和
for (int i = 0; i < n; i++) {
cin >> length >> probability;
sum += probability;
pro[i] = {length, probability, 0};
}

// 计算每个程序的权重
for (int i = 0; i < n; i++) {
pro[i].weight = static_cast<double>(pro[i].probability) / sum * pro[i].length;
}

// 按照权重升序排序
sort(pro, pro + n);

double res = 0;

// 计算最小平均读取时间
for (int r = 0; r < n; r++) {
double tr = 0;

// 累加前r个程序的权重,表示前r个程序的平均读取时间
for (int i = 0; i <= r; i++) {
tr += pro[i].weight;
}

res += tr;
}

// 输出最小平均读取时间
cout << res << endl;

return 0;
}

4.1
问题描述:
95枪族是我国现役主力步枪之一。假设某95步枪由 n 个部件组成,每一种部件都可以从 m 个不同的供应商处购得。设 wij 是从供应商 j 处购得的部件 i 的重量,cij 是相应的价格。
试设计一个算法,给出总价格不超过 c 的最小重量步枪的部分采购方案。
编程任务:
对于给定的步枪部件重量和步枪部件价格,编程计算总价格不超过 d 的最小重量步枪设计。
数据输入:
输入数据第一行有 3 个正整数 n ,m 和 d。接下来的 2n 行,每行 m 个数。前 n 行是 c,后 n 行是 w。
1 <= n, m <= 20。
结果输出:
将计算出的最小重量输入,并输出每个部件的供应商。
样例输入:
3 3 4
1 2 3
3 2 1
2 2 2
1 2 3
3 2 1
2 2 2
样例输出:
4
1 3 1
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 30;

int n, m, d; // 部件数量n,供应商数量m,总价格限制d
int c[N][N], w[N][N]; // c[i][j]表示部件i在供应商j处的价格,w[i][j]表示部件i在供应商j处的重量
int ans[N], temp[N]; // ans数组存储最小重量步枪的部分采购方案,temp数组用于暂存当前采购方案
int res = 1e9; // res存储最小重量

// 深度优先搜索函数
// 参数u表示当前处理部件的编号,v表示已经采购的价格,k表示当前总重量
void dfs(int u, int v, int k)
{
if (u > n) // 所有部件都已经处理完毕
{
if (k < res) // 更新最小重量和采购方案
{
res = k;
memcpy(ans, temp, sizeof ans);
}
return;
}
for (int i = 1; i <= m; i ++) // 遍历所有供应商
{
temp[u] = i; // 假设部件u从供应商i处采购
if (v + c[u][i] <= d && k + w[u][i] <= res) // 判断采购后的价格和重量是否满足限制条件
dfs(u + 1, v + c[u][i], k + w[u][i]); // 递归处理下一个部件
}
}

int main()
{
cin >> n >> m >> d; // 输入部件数量n,供应商数量m,总价格限制d
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= m; j ++)
cin >> c[i][j]; // 输入部件的价格
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= m; j ++)
cin >> w[i][j]; // 输入部件的重量
dfs(1, 0, 0); // 从第一个部件开始进行深度优先搜索
cout << res << endl; // 输出最小重量
for (int i = 1; i <= n; i ++) cout << ans[i] << ' '; // 输出部件的供应商

return 0;
}

 

【上大分,必须上大分】

 

作业二
1.问题描述
考虑下面的应用场景:
山东建筑大学烟台校区启用,现需要为n位同学分配宿舍,假设学校充分尊重同学们的意见,即可以任何x个同学使用一间宿舍(x>0),问:一共有多少种宿舍分配方案?

例如:n=3时,共有3位同学需要分配宿舍,假设为张三、李四、王五3位同学,则可以有以下5种分配方案:

可以每位同学一个宿舍
可以张三同学单独一间,其余两人共用一间
可以李四同学单独一间,其余两人共用一间
可以王五同学单独一间,其余两人共用一间
可以三位同学共用一间
假设学校宿舍的房间数不小于学生数量。

输入
学生数量n,n <= 100。

输出
所有分配方案的数量。

样例输入
3

样例输出
5

 

#include <iostream>
using namespace std;
typedef long long ll;
const int N = 110;
long long res[N][N] = {0}; // 用于存储组合数的数组
ll x;
ll ans;
// 计算组合数
ll C(ll n, ll m) {
// 如果选择0个或者选择全部,返回1
if (m == 0 || m == n)
return 1;

// 如果已经计算过,直接返回保存的结果
if (res[n][m] != 0)
return res[n][m];

// 递归计算组合数,并将结果保存在数组中
return res[n][m] = C(n - 1, m) + C(n - 1, m - 1); // 赋值给res[n][m]并返回
}

// 递归函数,计算宿舍分配方案
ll dfs(ll m) {
ll sum = 0;

// 如果只有一个人或者没有人,只有一种方案
if (m == 0 || m == 1)
return 1;

// 遍历可能的宿舍分配情况,计算总方案数
for (int i = 0; i < m; ++i) {
sum += C(m - 1, i) * dfs(i);
}
return sum;
}

int main() {
ll x;
ll ans;
cin >> x;

// 调用递归函数计算宿舍分配方案
ans = dfs(x);
cout << ans; // 输出结果
return 0;
}

2.问题描述
考虑下面的应用场景:
山东建筑大学烟台校区启用,现需要为n位同学分配至m间宿舍,问:一共有多少种宿舍分配方案?

例如:n=3,m=2时,共有3位同学需要分配到2间宿舍中,假设为张三、李四、王五3位同学,则可以有以下3种分配方案:

可以张三同学单独一间,其余两人共用一间
可以李四同学单独一间,其余两人共用一间
可以王五同学单独一间,其余两人共用一间
输入
学生数量n,n <= 100,房间数量m,m<=n。

输出
所有分配方案的数量。
#include <iostream>
using namespace std;
typedef long long ll;
ll x1, x2, ans;
// n个学生分配m个宿舍,保证每个宿舍至少分配一个学生
ll f(ll n, ll m) {
if (n < m)
return 0;
if (m == 1 || n == m)// 只有一个宿舍或者学生数量等于宿舍数量
return 1;

/*一个学生的分配情况:
(1)自己单独一个宿舍。剩余n-1个学生分配到m-1个宿舍中
(2)和其他人混合分配。n-1个学生分配m个宿舍,自己在m个宿舍中挑选一个
*/
return f(n - 1, m - 1) + m * f(n - 1, m);

}
int main() {
cin >> x1 >> x2;
ans = f(x1, x2);
cout << ans;
return 0;
}
作业三
1.任务描述
现有一个二维的金字塔,由若干方格组成,每个方格里的数字表明该方格的宝物价值。
你可以选择一条从塔顶到塔底的路线(每层只能走一个方格,该方格必须为上一层走过方格的相邻方格),并收集路线上所有方格的宝物,如何选择路线,使得最终收集到的宝物价值的和最大?
'
2
1 2
1 2 1

输入
第一行为金字塔的层数n(1≤n≤100)。
第二行开始,每行为金字塔每层宝物的价值,0≤价值≤99。

输出
输入你可以收集到的最大价值。


#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;
const int N = 110;

int n;
int f[N][N], w[N][N];

int main()
{
// 输入金字塔层数
cin >> n;

// 输入金字塔每层宝物的价值
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= i; ++j)
cin >> w[i][j];

// 动态规划计算最大宝物价值
f[1][1] = w[1][1];
for (int i = 2; i <= n; ++i)
for (int j = 1; j <= i; ++j)
{
f[i][j] = max(f[i - 1][j - 1] + w[i][j], f[i - 1][j] + w[i][j]);
}

// 找到最终最大宝物价值
int ans = 0;
for (int i = 1; i <= n; ++i)
ans = max(f[n][i], ans);

// 输出最大宝物价值
cout << ans << endl;

return 0;
}


2.问题描述:
给定一串十进制数字,共n位,要求将这些数字分成m段,由此得到了m个整数。如何划分使得m个整数的乘积最大?

数据输入:
第 1 行中包含n和m。
第2行是n位十进制整数。(n<=10)

数据输出:
输出最大乘积。

样例输入:
3 2
123

样例输出:
36

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 310;
int n, m, s; // 分别表示数字串长度、划分的段数、给定的数字串
int a[N]; // 存储数字串中每一位的数字
long long f[N][N], g[N][N]; // 动态规划中的状态数组,g数组用于存储数字串中子串的值

// 初始化g数组
void init_g()
{
for (int i = 1; i <= n; i ++)
{
g[i][i] = a[i];
for (int j = i + 1; j <= n; j ++)
g[i][j] = g[i][j - 1] * 10 + a[j];
}
}

int main()
{
// 输入n、m、s
scanf("%d%d%d", &n, &m, &s);

// 将数字s转换为字符数组,并存储到a数组中
string str = to_string(s);
for (int i = 0; i < str.size(); i ++ ) a[i + 1] = str[i] - '0';

// 初始化g数组
init_g();

for (int i = 1; i <= n; i ++)
{
f[i][1] = g[1][i];
for (int j = 2; j <= min(m, i); j ++)
for (int k = j - 1; k < i; k ++)
f[i][j] = max(f[i][j], f[k][j - 1] * g[k + 1][i]);
}

// 输出结果
printf("%lld\n", f[n][m]);

return 0;
}


作业四
1.任务描述
在一条直线上,有n个宝藏,每个宝藏的坐标是ai。其中ai为整数,n <= 10000

传说曹操手下有一批摸金校尉,每个摸金校尉可以收集长度为k的一段距离内的宝藏。比如,如果一个摸金校尉从坐标为x的位置开始收集,他可以收集[x,x+n]范围内的所有宝藏。

现给出n个宝藏的坐标,问至少需要多少摸金校尉才可以收集到所有的宝藏?

输入
第一行为n和k的值,n <= 10000。
接下来一行是n个整数,代表n个宝藏的坐标ai。

输出
输出所需要的最少的摸金校尉的个数。

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 10005;

int n, k;
int a[N];

int main()
{
// 输入宝藏数量n和摸金校尉能收集的范围k
cin >> n >> k;

// 输入n个宝藏的坐标
for (int i = 0; i < n; ++i)
cin >> a[i];

// 将宝藏坐标排序
sort(a, a + n);

// 输出排序后的宝藏坐标
cout << "Sorted Coordinates: ";
for (int i = 0; i < n; i++)
cout << a[i] << " ";
cout << endl;

int cnt = 0; // 记录摸金校尉的个数
int res = a[0] + k; // 初始范围右边界

for (int i = 0; i < n; i++)
{
// 在当前范围内找到最右边的宝藏
while (res >= a[i] && i < n)
i++;

cnt++; // 摸金校尉数量加一
res = a[i - 1] + k; // 更新范围右边界为上一个宝藏位置加上范围k
i--; // 因为循环末尾会再次自增,这里需要减回去
}

// 输出结果
cout << "Minimum Number of Explorers: " << cnt << endl;

return 0;
}


2.任务描述
设有 n 个顾客同时等待一项服务。顾客i 需要的服务时间为ti ,1 ≤ i ≤ n 。共有 s 处可以提供此项服务。应如何安排 n 个顾客的服务次序才能使平均等待时间达到最小?平均等待时间是 n 个顾客等待服务时间的总和除以 n。

编程任务:
对于给定的 n 个顾客需要的服务时间和 s 的值,编程计算最优服务次序。

数据输入:
输入数据第一行有 2 个正整数 n 和 s,表示有 n 个顾客且有 s 处可以提供顾客需要的服务。接下来的 1 行中,有 n 个正整数,表示 n 个顾客需要的服务时间。
1 ≤ n ≤ 10000

结果输出:
将计算出的最小平均等待时间输出。


#include <bits/stdc++.h>
using namespace std;

const int N = 10010;

int n, s;
int a[N], w[N]; // a为输入队列,w为服务队列,用区间[0, s)的哈希表,队列a所有元素都会分配到这s个桶中
int total;

int main()
{
// 输入顾客数量n和服务处数量s
cin >> n >> s;

// 输入每个顾客需要的服务时间
for (int i = 0; i < n; i++)
cin >> a[i];

// 将顾客需要的服务时间升序排序
sort(a, a + n);

for (int i = 0; i < n; i++)
{
int j = i % s; // 找到对应的服务桶号
w[j] = w[j] + a[i]; // 每个元素的等待时间 = 排队时间 + 服务时间
total += w[j]; // 累计总等待时间
}

// 计算平均等待时间并输出结果
cout << fixed << setprecision(2) << (double)total / n << endl;

return 0;
}

 

posted @   流滢  阅读(117)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示