如下所示的由正整数数字构成的三角形:
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
从三角形的顶部到底部有很多条不同的路径。对于每条路径,把路径上面的数加起来可以得到一个和,和最大的路径称为最佳路径。你的任务就是求出最佳路径上的数字之和。
注意:路径上的每一步只能从一个数走到下一层上和它最近的下边(正下方)的数或者右边(右下方)的数。
时间限制:1000
内存限制:65536
输入
第一行为三角形高度100>=h>=1,同时也是最底层边的数字的数目。 从第二行开始,每行为三角形相应行的数字,中间用空格分隔。
输出
最佳路径的长度数值。
样例输入
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
样例输出
30
提示
如何采用动态规划的思想,对问题进行分解。
这个问题可以使用动态规划来解决。我们可以定义一个二维数组dp
,其中dp[i][j]
表示从三角形顶部到第i
行第j
列的最佳路径的数字之和。
根据题目的要求,对于每个位置(i, j)
,可以从上方的位置(i-1, j-1)
或者(i-1, j)
到达。因此,状态转移方程可以表示为:
dp[i][j] = max(dp[i-1][j-1], dp[i-1][j]) + triangle[i][j]
其中,triangle[i][j]
表示三角形第i
行第j
列的数字。
以下是使用C语言实现的代码:
#include
#include
#define MAX_HEIGHT 100
int max(int a, int b) {
return (a > b) ? a : b;
}
int calculateMaxPath(int height, int triangle[][MAX_HEIGHT]) {
int dp[MAX_HEIGHT][MAX_HEIGHT];
// 初始化dp数组
dp[0][0] = triangle[0][0];
for (int i = 1; i < height; i++) {
dp[i][0] = dp[i - 1][0] + triangle[i][0];
dp[i][i] = dp[i - 1][i - 1] + triangle[i][i];
}
// 计算最佳路径的数字之和
for (int i = 2; i < height; i++) {
for (int j = 1; j < i; j++) {
dp[i][j] = max(dp[i - 1][j - 1], dp[i - 1][j]) + triangle[i][j];
}
}
// 找出最大路径的数字之和
int maxSum = 0;
for (int j = 0; j < height; j++) {
maxSum = max(maxSum, dp[height - 1][j]);
}
return maxSum;
}
int main() {
int height;
scanf("%d", &height);
int triangle[MAX_HEIGHT][MAX_HEIGHT];
for (int i = 0; i < height; i++) {
for (int j = 0; j <= i; j++) {
scanf("%d", &triangle[i][j]);
}
}
int result = calculateMaxPath(height, triangle);
printf("%d\n", result);
return 0;
}
在主函数中,我们首先读取输入的三角形的高度height
,然后读取三角形的数字并存储在二维数组triangle
中。接下来,我们调用calculateMaxPath
函数计算最佳路径的数字之和,并将结果输出。
小A是个邮票收集爱好家,他有n种面值的邮票,每种邮票都有无数张。一天小B想要寄信,需要一共面值和为k的邮票组合。小A想要知道拼出面值为k的邮票最少需要多少张。
时间限制:1000
内存限制:131072
输入
输入是多组数据。(不超过10组) 每组数据的第一行正整数n,k,表示邮票的种类数目和目标要拼出的钱。(0 < n ≤ 100, 0 < k ≤ 1000 ) 接下来的一行有n个正整数ai(0 < ai ≤ 1000)。 若n=k=0表示输入结束。
输出
每组数据输出一行一个数,分别表示拼出k需要的最少的邮票数量。 如果不存在能够拼出k的方案,输出-1。
样例输入
4 10
1 2 3 4
5 16
1 2 3 4 5
2 7
4 5
0 0
样例输出
3
4
-1
提示
第一组数据: 10 = 4+4+2 第二组数据:16 = 5+5+5+1 第三组数据: 不存在。
这个问题可以使用动态规划来解决。我们可以定义一个一维数组dp
,其中dp[i]
表示拼出面值为i
的邮票所需的最少数量。
根据题目的要求,对于每个金额i
,我们可以考虑选择每种面值的邮票,然后更新最少数量。状态转移方程可以表示为:
dp[i] = min(dp[i], dp[i - a[j]] + 1), 其中 0 <= j < n
其中,a[j]
表示第j
种面值的邮票。
以下是使用C语言实现的代码:
#include
#include
#include
#define MAX_N 100
#define MAX_K 1000
int min(int a, int b) {
return (a < b) ? a : b;
}
int calculateMinStamps(int n, int k, int stamps[]) {
int dp[MAX_K + 1];
// 初始化dp数组
dp[0] = 0;
for (int i = 1; i <= k; i++) {
dp[i] = INT_MAX;
}
// 计算最少邮票数量
for (int i = 1; i <= k; i++) {
for (int j = 0; j < n; j++) {
if (i >= stamps[j] && dp[i - stamps[j]] != INT_MAX) {
dp[i] = min(dp[i], dp[i - stamps[j]] + 1);
}
}
}
return (dp[k] == INT_MAX) ? -1 : dp[k];
}
int main() {
int n, k;
while (scanf("%d %d", &n, &k) == 2) {
if (n == 0 && k == 0) {
break; // 输入结束
}
int stamps[MAX_N];
for (int i = 0; i < n; i++) {
scanf("%d", &stamps[i]);
}
int result = calculateMinStamps(n, k, stamps);
printf("%d\n", result);
}
return 0;
}
在主函数中,我们使用一个循环来读取多组数据。对于每组数据,我们首先读取邮票的种类数目n
和目标金额k
,然后读取每种面值的邮票,并将它们存储在数组stamps
中。接下来,我们调用calculateMinStamps
函数计算拼出目标金额所需的最少邮票数量,并将结果输出。
阿福最近对回文串产生了非常浓厚的兴趣。
如果一个字符串从左往右看和从右往左看完全相同的话,那么就认为这个串是一个回文串。例如,“abcaacba”是一个回文串,“abcaaba”则不是一个回文串。
阿福现在强迫症发作,看到什么字符串都想要把它变成回文的。阿福可以通过切割字符串,使得切割完之后得到的子串都是回文的。
现在阿福想知道他最少切割多少次就可以达到目的。例如,对于字符串“abaacca”,最少切割一次,就可以得到“aba”和“acca”这两个回文子串。
时间限制:1000
内存限制:65536
输入
输入的第一行是一个整数 T (T <= 20) ,表示一共有 T 组数据。 接下来的 T 行,每一行都包含了一个长度不超过的 1000 的字符串,且字符串只包含了小写字母。
输出
对于每组数据,输出一行。该行包含一个整数,表示阿福最少切割的次数,使得切割完得到的子串都是回文的。
样例输入
3
abaacca
abcd
abcba
样例输出
1
3
0
提示
对于第一组样例,阿福最少切割 1 次,将原串切割为“aba”和“acca”两个回文子串。 对于第二组样例,阿福最少切割 3 次,将原串切割为“a”、“b”、“c”、“d”这四个回文子串。 对于第三组样例,阿福不需要切割,原串本身就是一个回文串。
这个问题可以使用动态规划来解决。我们可以定义一个一维数组dp
,其中dp[i]
表示前i
个字符组成的子串最少需要切割几次才能使得子串都是回文的。
根据题目的要求,对于每个位置i
,我们可以考虑将子串切割为两部分,前半部分为回文子串,后半部分为回文子串。如果前半部分是回文子串,那么我们只需要判断后半部分是否是回文子串,即判断dp[j] + 1
是否更小。状态转移方程可以表示为:
dp[i] = min(dp[i], dp[j] + 1), 其中 j < i 且 s[j+1...i]是回文子串
其中,s[j+1...i]
表示字符串中从位置j+1
到位置i
的子串。
以下是使用C语言实现的代码:
#include
#include
#include
#define MAX_LEN 1000
int min(int a, int b) {
return (a < b) ? a : b;
}
int isPalindrome(char str[], int start, int end) {
while (start < end) {
if (str[start] != str[end]) {
return 0; // 不是回文串
}
start++;
end--;
}
return 1; // 是回文串
}
int calculateMinCut(char str[]) {
int len = strlen(str);
int dp[MAX_LEN];
int isPal[MAX_LEN][MAX_LEN];
// 初始化dp数组和isPal数组
for (int i = 0; i < len; i++) {
dp[i] = i; // 最多切割i次
for (int j = 0; j < len; j++) {
isPal[i][j] = 0;
}
}
// 计算最少切割次数
for (int i = 0; i < len; i++) {
for (int j = 0; j <= i; j++) {
if (str[i] == str[j] && (i - j <= 2 || isPal[j + 1][i - 1])) {
isPal[j][i] = 1;
if (j == 0) {
dp[i] = 0; // 整个子串是回文串,不需要切割
} else {
dp[i] = min(dp[i], dp[j - 1] + 1);
}
}
}
}
return dp[len - 1];
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
char str[MAX_LEN];
scanf("%s", str);
int result = calculateMinCut(str);
printf("%d\n", result);
}
return 0;
}
在主函数中,我们首先读取整数T
,表示有T
组数据。然后,使用一个循环读取每组数据的字符串,并调用calculateMinCut
函数计算最少切割次数,并将结果输出。
有N个相同的球,M个不同的盒子,每个盒子最多放K个球
请计算将这N个球全部放入盒子中的方案数模1000007后的结果
时间限制:10000
内存限制:131072
输入
三个正整数,依次为N,M,K
输出
输出方案数模1000007后的结果
样例输入
4 2 3
样例输出
3
提示
总共有3种方案,依次为 { 3 , 1 },{ 2 , 2 },{ 1 , 3 }。 对于100%的数据, N,M ≤ 5000
这个问题可以使用组合数学的知识来解决。我们需要计算将N个球放入M个盒子中的方案数。
首先,考虑将N个球放入一个盒子中的方案数。由于每个盒子最多放K个球,我们可以使用0个、1个、2个…K个球来填充这个盒子。因此,对于一个盒子,可以有K+1种放球的方案。
接下来,考虑将N个球放入M个盒子中的方案数。我们可以将问题转化为将N个球放入M个盒子中,每个盒子至少放1个球的方案数。假设我们已经将每个盒子放入了一个球,那么剩余的N-M个球可以按照第一个考虑的情况放入这M个盒子中。根据乘法原理,将N-M个球放入M个盒子中的方案数为(M+1)^(N-M)。
因此,将N个球放入M个盒子中的方案数为(M+1)^(N-M)。最终的结果需要对1000007取模。
以下是使用C语言实现的代码:
#include
int powerMod(int base, int exponent, int mod) {
int result = 1;
while (exponent > 0) {
if (exponent % 2 == 1) {
result = (result * base) % mod;
}
base = (base * base) % mod;
exponent /= 2;
}
return result;
}
int calculateBallInBox(int N, int M, int K) {
int result = powerMod(M + 1, N - M, 1000007);
return result;
}
int main() {
int N, M, K;
scanf("%d %d %d", &N, &M, &K);
int result = calculateBallInBox(N, M, K);
printf("%d\n", result);
return 0;
}
在主函数中,我们首先读取整数N、M、K,分别表示球的数量、盒子的数量和每个盒子最多放置的球的数量。然后,调用calculateBallInBox
函数计算将N个球放入M个盒子中的方案数,并将结果输出。