问题 A: 简单递归求和
题目描述
使用递归编写一个程序求如下表达式前n项的计算结果: (n<=100)
1 - 3 + 5 - 7 + 9 - 11 +......
输入n,输出表达式的计算结果。
输入
多组输入,每组输入一个n,n<=100。
输出
输出表达式的计算结果。
样例输入 Copy
1 2
样例输出 Copy
1 -2
#include<stdio.h>
int fun(int n) {
if (n == 0) return 0;
else if (n == 1) return 1;
else if (n % 2 == 0) return fun(n-1) - (2 * n - 1);
else return fun(n-1)+(2 * n - 1);
}
int main() {
int n;
while (~scanf("%d", &n)) {
int sum = fun(n);
printf("%d\n", sum);
}
return 0;
}
问题 B: 递归求和
题目描述
使用递归编写一个程序求如下表达式的计算结果: (1<n<=20)
S(n) = 1*4 + 4*9 + 9*16 + 16*25 + ... + ((n-1)^2)*n^2
输入n,输出表达式S(n)的结果。
输入
单组输入,输入一个正整数n,1<n<=20。
输出
输出表达式S(n)的计算结果。
样例输入 Copy
3
样例输出 Copy
40
#include<stdio.h>
int fun(int n) {
if (n == 2) return 4;
else return fun(n - 1) + ((n - 1) * (n - 1) * n * n);
}
int main() {
int n;
while (~scanf("%d", &n)) {
int sum = fun(n);
printf("%d\n", sum);
}
return 0;
}
问题 C: 文件存储
题目描述
如果有n个文件{F1,F2,F3,…,Fn}需要存放在大小为M的U盘中,文件i的大小为Si,1<=i<=n。请设计一个算法来提供一个存储方案,使得U盘中存储的文件数量最多。
输入
多组输入,对于每组测试数据,每1行的第1个数字表示U盘的容量M(以MB为单位,不超过256*1000MB),第2个数字表示待存储的文件个数n。
第2行表示待存储的n个文件的大小(以MB为单位)。
输出
输出最多可以存放的文件个数。
样例输入 Copy
10000 5 2000 1000 5000 3000 4000
样例输出 Copy
4
这道题目是一个经典的背包问题,可以使用动态规划算法解决。动态规划算法主要是将问题划分成子问题并逐步求解,最后得出原问题的解。
首先,在主函数中读入U盘容量m和文件数量n,然后读入每个文件的大小,并将文件大小存储在一个数组s中。
接下来,使用dp数组记录每个状态的最优解,dp[j]表示当前容量为j的时候,可以存储的最多文件数量。从容量为m的状态开始循环,根据状态转移方程dp[j] = dp[j] > dp[j - s[i]] + 1 ? dp[j] : dp[j - s[i]] + 1,计算出下一个状态的最优解。
在循环结束后,返回dp[m],即为最多可以存放的文件个数。
由于背包问题的时间复杂度为O(nm),可以保证在U盘容量不超过256000MB时快速得到结果。
#include <stdio.h>
#include <string.h>
#define MAXN 1005
/**
* 背包问题
* 参数:
* n:文件数量
* m:U盘容量
* s[]:文件大小数组
* 返回值:最多可以存放的文件个数
*/
int knapsack(int n, int m, int s[]) {
int dp[256 * 1000]; // dp数组用于记录每个状态的最优解
memset(dp, 0, sizeof(dp)); // 初始化dp数组全为0
int i, j;
for (i = 1; i <= n; ++i) {
for (j = m; j >= s[i]; --j) {
// 状态转移方程
dp[j] = dp[j] > dp[j - s[i]] + 1 ? dp[j] : dp[j - s[i]] + 1;
}
}
return dp[m]; // 返回最优解
}
int main() {
int m, n;
int s[MAXN];
while (~scanf("%d%d", &m, &n)) {
for (int i = 1; i <= n; i++) {
scanf("%d", &s[i]);
}
printf("%d\n", knapsack(n, m, s));
}
return 0;
}
问题 D: 图的m着色问题
题目描述
给定无向连通图G和m种不同的颜色。用这些颜色为图G的各顶点着色,每个顶点着一种颜色。是否有一种着色法使G中每条边的2个顶点着不同颜色,请输出着色方案。
输入
输入第一行包含n,m,k分别代表n个结点,m条边,k种颜色,接下来m行每行有2个数u,v表示u和v之间有一条无向边,可能出现自环边,所以请忽略自环边。
输出
输出所有不同的着色方案,且按照字典序从小到大输出方案。
样例输入 Copy
3 3 3 1 2 1 3 2 3
样例输出 Copy
1 2 3 1 3 2 2 1 3 2 3 1 3 1 2 3 2 1
首先,在主函数中读入图的顶点数量n、边的数量m和颜色种类数量k,并且使用邻接矩阵adj数组记录图的边。然后,调用dfs函数从第一个顶点开始搜索可行的着色方案。
dfs函数的实现思路是:对于每一个顶点cur,从1~k枚举所有可能的颜色,检查与该顶点相邻的顶点的颜色是否相同,如果不同,则将cur顶点着上该颜色,然后递归进入下一个顶点。当所有顶点都着色完毕时,输出当前的着色方案,并将可行的着色方案数量count加1。如果当前顶点的所有颜色都不能满足要求,就需要返回到上一个状态,重新选择上一个顶点的颜色。
在dfs函数结束后,如果找到了可行的着色方案,则将其按照字典序从小到大输出。如果没有可行的着色方案,则输出"No solution."。
由于回溯算法的时间复杂度比较高,对于顶点数量较大的图,可能需要较长的时间才能得到结果。
#include<stdio.h>
#define MAXN 105
#define MAXM 10005
int n, m, k;
int adj[MAXN][MAXN];
int colors[MAXN];
int count; // 记录可行的着色方案数量
void dfs(int cur) {
if (cur > n) {
for (int i = 1; i <= n; i++) {
printf("%d ", colors[i]);
}
printf("\n");
count++;
return;
}
for (int i = 1; i <= k; i++) {
int flag = 1;
for (int j = 1; j < cur; j++) {
if (adj[cur][j] == 1 && colors[j] == i) {
flag = 0; // 检查相邻顶点颜色是否相同
break;
}
}
if (flag) {
colors[cur] = i; // 着色
dfs(cur + 1);
colors[cur] = 0; // 回溯
}
}
}
int main() {
scanf("%d%d%d", &n, &m, &k);
for (int i = 1; i <= n; i++) {
colors[i] = 0;
for (int j = 1; j <= n; j++) {
adj[i][j] = 0;
}
}
for (int i = 1; i <= m; i++) {
int u, v;
scanf("%d%d", &u, &v);
adj[u][v] = 1;
adj[v][u] = 1;
}
dfs(1);
if (count == 0) {
printf("No solution.\n");
}
return 0;
}
问题 E: 素数环
题目描述
输入
输入正整数n。
输出
注:每一个环都从1开始。
样例输入 Copy
6
样例输出 Copy
1 4 3 2 5 6 1 6 5 2 3 4
这道题可以用回溯的方法求解,步骤如下:
- 从1开始尝试构造环,逐步向后搜索;
- 当只剩下一个数时,检查第一个数与最后一个数之和是否为素数,如果是则表示已经找到了一个环;
- 尝试从剩余的数中选择下一个数,并检查当前数与前一个数之和是否为素数,如果不是则放弃这个选择继续尝试新的选择;
- 如果所有选择都不能满足条件,则回溯到上一层继续进行尝试;
- 将环的情况保存在一个数组中,防止重复输出,使用字典序排序。
思路解析:
回溯函数backtrack
从1开始尝试构造环,使用一个visited数组来记录哪些数已经被选中。如果只剩下一个数,则检查第一个数和最后一个数之和是否为素数,如果是则表示找到了一个环,执行print_solution
函数输出环的情况。如果没有构造完整个环,则尝试从剩余的数中选择下一个数,并检查当前数是否能够与前一个数形成素数。如果当前数不能与前一个数形成素数,则放弃这个选择,继续尝试其他的选择;如果可以形成素数,则将当前数添加到解x中,并将visited数组对应的值设为1,递归到下一层继续进行尝试。如果所有的选择都不能满足条件,则回溯到上一层继续进行尝试。
由于同一个环只需要输出一次,需要使用一个
#include <stdio.h>
#include <stdlib.h>
static int n, count;
static int* x, * visited; // x数组存放回溯过程中已经选择的数,visited数组存放某个数是否已经被选择
/**
* 判断一个数是否为素数
*/
int is_prime(int n) {
int i;
if (n < 2) {
return 0;
}
for (i = 2; i <= n / 2; ++i) {
if (n % i == 0) {
return 0;
}
}
return 1;
}
/**
* 输出一组环
*/
void print_solution() {
int i;
for (i = 0; i < n; ++i) {
printf("%d ", x[i]);
}
printf("\n");
++count;
}
/**
* 回溯函数,尝试构造环
*/
void backtrack(int k) {
int i;
if (k == n) {
if (is_prime(x[0] + x[n - 1])) {
print_solution(); // 找到一个环
}
}
else {
for (i = 2; i <= n; ++i) {
if (!visited[i]) {
// 检查当前数与前一个数是否为素数
if (k == 0 || is_prime(i + x[k - 1])) {
x[k] = i;
visited[i] = 1;
backtrack(k + 1); // 递归到下一层
visited[i] = 0;
}
}
}
}
}
/**
* 按字典序比较两个环的大小
*/
int cmp(const void* a, const void* b) {
int i;
int* pa = (int*)a, * pb = (int*)b;
for (i = 0; i < n; ++i) {
if (pa[i] != pb[i]) {
return pa[i] - pb[i];
}
}
return 0;
}
int main() {
int i;
scanf("%d", &n);
x = (int*)malloc(n * sizeof(int));
visited = (int*)calloc(n + 1, sizeof(int));
x[0] = 1; // 第一个数是1
visited[1] = 1; // 已选中1
backtrack(1);
free(x);
free(visited);
return 0;
}
问题 F: N皇后问题
题目描述
使用回溯法求解N后问题。
输入
皇后的个数。
输出
每一种方案及总方案数。
样例输入 Copy
4
样例输出 Copy
0 1 0 0 0 0 0 2 3 0 0 0 0 0 4 0 ---------------- 0 0 1 0 2 0 0 0 0 0 0 3 0 4 0 0 ---------------- 总方案数为:2
使用回溯法求解N后问题的步骤如下:
- 从第一列开始,依次放置每个皇后;
- 如果当前列放置了皇后,就不能再在该列放置皇后;
- 如果当前行放置皇后后,与之前放置的皇后产生了冲突(在同一行或同一对角线上),则撤回该行的皇后;
- 如果已经放置了n个皇后,则表示找到了一组解;
- 递归到下一行,继续放置皇后。
思路解析:
该代码中的check
函数用于检查当前放置的皇后是否与之前放置的皇后产生冲突。如果在同一行或同一对角线上,就会产生冲突,返回0
;否则,返回1
。在
backtrack
函数中,尝试放置第k个皇后,如果不产生冲突,就递归到下一行继续放置皇后。如果已经放置了n个皇后,就表示找到了一组解,输出后计数器count
加 1。如果所有的情况都尝试完毕,还没有找到合法的解,则函数返回上一级,进行回溯。
#include <stdio.h>
#include <stdlib.h>
static int n; // 皇后的个数
static int count; // 方案数
static int* x; // 存放解
/**
* 检查x[k]是否与前面的皇后产生冲突
*/
int check(int k) {
int i;
for (i = 0; i < k; ++i) {
if (x[i] == x[k] || abs(x[i] - x[k]) == abs(i - k)) {
return 0; // 产生冲突,返回0
}
}
return 1; // 未产生冲突,返回1
}
/**
* 输出一组解
*/
void print_solution() {
int i, j;
for (i = 0; i < n; ++i) {
for (j = 0; j < n; ++j) {
if (x[i] == j) {
printf("%d ", i + 1);
}
else {
printf("0 ");
}
}
printf("\n");
}
printf("----------------\n");
++count;
}
/**
* 回溯函数,尝试放置第k个皇后
*/
void backtrack(int k) {
int i;
if (k == n) {
print_solution(); // 找到一组解
}
else {
for (i = 0; i < n; ++i) { // 尝试放置第k个皇后
x[k] = i;
if (check(k)) { // 如果不冲突,继续递归
backtrack(k + 1);
}
}
}
}
int main() {
int i;
scanf("%d", &n);
x = (int*)malloc(n * sizeof(int));
for (i = 0; i < n; ++i) {
x[i] = -1; // 初始化为-1
}
backtrack(0); // 从第0行开始放置皇后
printf("总方案数为:%d\n", count);
free(x); // 释放内存
return 0;
}