53:S01串
作者: turbo
时间限制: 1s
章节: 字符串
问题描述
s01串初始为"0"
按以下方式变换:
0变1,1变01
所以,变换规律如下:
1次变换后:1;
2次变换后:01;
3次变换后:101;
4次变换后:01101;
5次变换后:10101101;
......
输入说明
输入1个整数n,范围为0<=n<=19,表示变换的次数
输出说明
n次变换后的s01串,行首行尾无空格,也无空行。
cpp
#include <stdio.h>
#include <string.h>
int main(){
int n;
scanf("%d", &n);
char str[10000] = {0};
str[0] = '0';
str[1] = '\0';
int count = 0;
while(count < n){
int len = strlen(str);
char new_str[1000000];
int idx = 0;
for(int i = 0; i < len; i++){
if(str[i] == '0')
new_str[idx++] = '1';
else{
new_str[idx++] = '0';
new_str[idx++] = '1';
}
}
new_str[idx] = '\0';
strcpy(str, new_str);
count++;
}
printf("%s", str);
return 0;
}
54:身份证排序
作者: Turbo
时间限制: 1s
章节: 基本练习(字符串)
问题描述
安全局搜索到了一批(n个)身份证号码,希望按出生日期对它们进行从大到小排序,如果有相同日期,则按身份证号码从大到小进行排序。身份证号码为18位的数字组成,出生日期为第7到第14位
输入说明
第一行一个整数n,表示有n个身份证号码
余下的n行,每行一个身份证号码。
n<=100000
输出说明
按出生日期从大到小排序后的身份证号,每行一条
cpp
#include <stdio.h>
#include <string.h>
int main() {
int n;
scanf("%d", &n);
char ids[n][20];
char dates[n][9];
for(int i = 0; i < n; i++){
scanf("%s", ids[i]);
strncpy(dates[i], ids[i] + 6, 8);
dates[i][8] = '\0';
}
for(int i = 0; i < n - 1; i++){
for(int j = i + 1; j < n; j++){
if(strcmp(dates[i], dates[j]) < 0){
char temp[20];
strcpy(temp, ids[i]);
strcpy(ids[i], ids[j]);
strcpy(ids[j], temp);
strcpy(temp, dates[i]);
strcpy(dates[i], dates[j]);
strcpy(dates[j], temp);
}
else if(strcmp(dates[i], dates[j]) == 0){
if(strcmp(ids[i], ids[j]) < 0){
char temp[20];
strcpy(temp, ids[i]);
strcpy(ids[i], ids[j]);
strcpy(ids[j], temp);
strcpy(temp, dates[i]);
strcpy(dates[i], dates[j]);
strcpy(dates[j], temp);
}
}
}
}
for(int i = 0; i < n; i++)
printf("%s\n", ids[i]);
return 0;
}
55:回文数
作者: Turbo
时间限制: 1s
章节: 模拟
问题描述
若一个数(首位不为零)从左向右读与从右向左读都一样,我们就将其称之为回文数。
我们现在需要产生回文数,步骤如下:
给定一个10进制数56,将56加65(即把56从右向左读),得到121是一个回文数。
又如:对于10进制数87:
STEP1:87+78 = 165
STEP2:165+561 = 726
STEP3:726+627 = 1353
STEP4:1353+3531 = 4884
在这里的"一步"是指进行了一次N进制的加法,上例最少用了4步得到回文数4884。
写一个程序,给定一个N(2<=N<=10或N=16)进制数M(其中16进制数字为0-9与A-F),求最少经过几步可以得到回文数。
如果在30步以内(包含30步)不可能得到回文数,则输出"Impossible!"
输入说明
两行,N与M
输出说明
如果能在30步以内得到回文数,输出"STEP=xx"(不含引号),其中xx是步数;否则输出一行"Impossible!"(不含引号)
cpp
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAX_LEN 1000
int toDigit(char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
return 0;
}
char toChar(int n) {
if (n < 10) return n + '0';
return n - 10 + 'A';
}
int isPal(char *s) {
int len = strlen(s);
for (int i = 0; i < len / 2; i++) {
if (s[i] != s[len - 1 - i]) return 0;
}
return 1;
}
int main() {
int N;
char M[MAX_LEN];
scanf("%d %s", &N, M);
char num[MAX_LEN], rev[MAX_LEN];
strcpy(num, M);
for (int step = 0; step <= 30; step++) {
if (isPal(num)) {
printf("STEP=%d\n", step);
return 0;
}
if (step == 30) break;
int len = strlen(num);
for (int i = 0; i < len; i++)
rev[i] = num[len - 1 - i];
rev[len] = '\0';
int carry = 0;
char result[MAX_LEN] = {0};
int idx = 0;
for (int i = len - 1; i >= 0 || carry; i--) {
int a = (i >= 0) ? toDigit(num[i]) : 0;
int b = (i >= 0) ? toDigit(rev[i]) : 0;
int sum = a + b + carry;
result[idx++] = toChar(sum % N);
carry = sum / N;
}
strcpy(num, result);
}
printf("Impossible!\n");
return 0;
}
56:新生舞会
作者: Turbo
时间限制: 1s
章节: 结构体
问题描述
新生舞会开始了。n名新生每人有三个属性:姓名、学号、性别。其中,姓名用长度不超过20的仅由大小写字母构成的字符串表示,学号用长度不超过10的仅由数字构成的字符串表示,性别用一个大写字符'F'或'M'表示。任意两人的姓名、学号均互不相同。换言之,每个人可被其姓名或学号唯一确定。
输入m对舞伴的信息(姓名或学号),判断他们是否能共舞。两人能共舞的充要条件为两人性别相异。
输入说明
第一行一个整数n(2<=n<=1000),表示学生人数。接下来的n行每行依次包含一名新生的姓名、学号、性别,分别用一个空格隔开。
之后的一行是一个整数m(1<=m<=1000),表示询问舞伴的对数。接着的m行每行包含两个舞伴的信息(姓名或学号),保证两个信息不属于同一人,中间用一个空格隔开。
输出说明
对于每个询问输出一行,如果两人可以共舞,输出一个大写字母'Y',否则输出一个大写字母'N'。
cpp
#include <stdio.h>
#include <string.h>
typedef struct{
char name[20];
char id[10];
char xb;
}Student;
int main(){
int n;
scanf("%d", &n);
Student stu[n];
for(int i = 0; i < n; i++)
scanf("%s %s %c", stu[i].name, stu[i].id, &stu[i].xb);
int m;
scanf("%d", &m);
char a[20], b[20];
for(int i = 0; i < m; i++){
scanf("%s %s", a, b);
int x, y;
for(int j = 0; j < n; j++){
if(strcmp(a, stu[j].name) == 0 || strcmp(a, stu[j].id) == 0){
x = j;
break;
}
}
for(int j = 0; j < n; j++){
if(strcmp(b, stu[j].name) == 0 || strcmp(b, stu[j].id) == 0){
y = j;
break;
}
}
if(stu[x].xb != stu[y].xb)
printf("Y\n");
else
printf("N\n");
}
return 0;
}
57:班级排名
作者: Turbo
时间限制: 1s
章节: 结构体
问题描述
达达在陶陶的影响下,也对学习慢慢的产生了兴趣。
他在每次考试之后,都会追着老师问,自己在班级的总名次是多少。考试一多,老师也不耐烦了,于是他给了达达所有人的成绩,让他自己去算出自己的排名。
可人太多了,达达也无法立即算出来,于是他想让你帮帮他。
输入说明
第一行为一个整数N,代表班级的学生总数。
接下来N行,每行一个字符串,代表一个学生的姓名,第一行总是DaDa。
接下来一行一个整数M,代表一共有M次考试。
每次考试有N行,每行有以一个空格分隔的一个正整数S和一个字符串P,代表名字为P的学生在这次考试中得了S分。
N <= 100,名字长度不超过30,分数不超过100
输出说明
一共M行,每行一个整数,代表达达在班级里的排名,排名是这一次考试过后的所有考试总分排名,如果达达和别人并列,达达总是排在前面。
cpp
#include <stdio.h>
#include <string.h>
typedef struct{
char name[35];
int grade;
}Student;
int main(){
int n;
scanf("%d", &n);
Student stu[n];
for(int i = 0; i < n; i++){
scanf("%s", stu[i].name);
stu[i].grade = 0;
}
int m;
scanf("%d", &m);
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
int grade;
char name[35];
scanf("%d %s", &grade, name);
for(int k = 0; k < n; k++){
if(strcmp(name, stu[k].name) == 0){
stu[k].grade += grade;
break;
}
}
}
for(int j = 0; j < n - 1; j++){
for(int k = j + 1; k < n; k++){
if(stu[j].grade < stu[k].grade){
Student temp = stu[j];
stu[j] = stu[k];
stu[k] = temp;
}
else if(stu[j].grade == stu[k].grade){
if(strcmp("DaDa", stu[k].name) == 0){
Student temp = stu[j];
stu[j] = stu[k];
stu[k] = temp;
}
}
}
}
int count = 0;
for(int j = 0; j < n; j++){
count++;
if(strcmp("DaDa", stu[j].name) == 0){
printf("%d\n", count);
break;
}
}
}
return 0;
}
58:铺地毯
作者: Turbo
时间限制: 1s
章节: 结构体
问题描述
为了准备一个学生节,组织者在会场的一片矩形区域(可看做是平面直角坐标
系的第一象限)铺上一些矩形地毯。一共有n 张地毯,编号从1 到n。现在将这些地毯按照
编号从小到大的顺序平行于坐标轴先后铺设,后铺的地毯覆盖在前面已经铺好的地毯之上。
地毯铺设完成后,组织者想知道覆盖地面某个点的最上面的那张地毯的编号。注意:在矩形
地毯边界和四个顶点上的点也算被地毯覆盖。
输入说明
输入共 n+2 行。
第一行,一个整数 n,表示总共有n 张地毯。
接下来的 n 行中,第i+1 行表示编号i 的地毯的信息,包含四个正整数a,b,g,k,每
两个整数之间用一个空格隔开,分别表示铺设地毯的左下角的坐标(a,b)以及地毯在x
轴和y 轴方向的长度。
第 n+2 行包含两个正整数x 和y,表示所求的地面的点的坐标(x,y)。
0≤n≤10,000,0≤a, b, g, k≤100,000。
输出说明
输出共 1 行,一个整数,表示所求的地毯的编号;若此处没有被地毯覆盖则输出-1。
cpp
#include <stdio.h>
typedef struct {
int a; // 左下角x坐标
int b; // 左下角y坐标
int g; // x轴方向长度
int k; // y轴方向长度
} Carpet;
int main() {
int n;
scanf("%d", &n);
Carpet carpets[10005]; // n最大10000
for (int i = 1; i <= n; i++) {
scanf("%d %d %d %d", &carpets[i].a, &carpets[i].b,
&carpets[i].g, &carpets[i].k);
}
int x, y;
scanf("%d %d", &x, &y);
int result = -1; // 初始化为-1,表示没有被覆盖
// 从后往前遍历地毯(因为后铺的在上层)
for (int i = n; i >= 1; i--) {
// 检查点(x,y)是否在地毯i内
if (x >= carpets[i].a && x <= carpets[i].a + carpets[i].g &&
y >= carpets[i].b && y <= carpets[i].b + carpets[i].k) {
result = i;
break; // 找到最上面的地毯,直接退出
}
}
printf("%d\n", result);
return 0;
}
59:最小乘积
作者: Turbo
时间限制: 1s
章节: 枚举
问题描述
给两组数,各n个。
请调整每组数的排列顺序,使得两组数据相同下标元素对应相乘,然后相加的和最小。要求程序输出这个最小值。
例如两组数分别为:1 3 -5和-2 4 1
那么对应乘积取和的最小值应为:
(-5) * 4 + 3 * (-2) + 1 * 1 = -25
输入说明
第一个行一个数T表示数据组数。后面每组数据,先读入一个n,接下来两行每行n个数,每个数的绝对值小于等于1000。
n<=8,T<=1000
输出说明
一个数表示答案。
cpp
#include <stdio.h>
#include <stdlib.h>
// 比较函数,用于升序排序
int cmp_asc(const void* a, const void* b) {
return *(int*)a - *(int*)b;
}
// 比较函数,用于降序排序
int cmp_desc(const void* a, const void* b) {
return *(int*)b - *(int*)a;
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
int n;
scanf("%d", &n);
int a[10], b[10]; // n <= 8
// 读取数组a
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
// 读取数组b
for (int i = 0; i < n; i++) {
scanf("%d", &b[i]);
}
// 对数组a进行升序排序
qsort(a, n, sizeof(int), cmp_asc);
// 对数组b进行降序排序
qsort(b, n, sizeof(int), cmp_desc);
// 计算最小乘积和
long long sum = 0; // 用long long防止溢出
for (int i = 0; i < n; i++) {
sum += (long long)a[i] * b[i];
}
printf("%lld\n", sum);
}
return 0;
}
60:排队打水问题
作者: Turbo
时间限制: 1s
章节: 贪心
问题描述
有n个人排队到r个水龙头去打水,他们装满水桶的时间t1、t2...........tn为整数且各不相等,应如何安排他们的打水顺序才能使他们总共花费的时间最少?
输入说明
第一行n,r (n<=500,r<=75)
第二行为n个人打水所用的时间Ti (Ti<=100);
输出说明
最少的花费时间
cpp
#include <stdio.h>
int main(){
int n, r;
scanf("%d %d", &n, &r);
int time[n];
for(int i = 0; i < n; i++)
scanf("%d", &time[i]);
//按打水时间从小到大排序
for(int i = 0; i < n - 1; i++){
for(int j = i + 1; j < n; j++){
if(time[i] > time[j]){
int temp = time[i];
time[i] = time[j];
time[j] = temp;
}
}
}
int taps[r];
for(int i = 0; i < r; i++)
taps[i] = 0;
int total_time = 0;
//给每个人分配水龙头
for(int i = 0; i < n; i++){
//找当前时间最短的水龙头
int min_tap = 0;
for(int j = 1; j < r; j++){
if(taps[j] < taps[min_tap])
min_tap = j;
}
//把这个人分配到该水龙头
taps[min_tap] += time[i];
total_time += taps[min_tap];
}
printf("%d\n", total_time);
return 0;
}
66:王、后传说
作者: turbo
时间限制: 1s
章节: 递归
问题描述
地球人都知道,在国际象棋中,后如同太阳,光芒四射,威风八面,它能控制横、竖、斜线位置。
看过清宫戏的中国人都知道,后宫乃步步惊心的险恶之地。各皇后都有自己的势力范围,但也总能找到相安无事的办法。
所有中国人都知道,皇权神圣,伴君如伴虎,触龙颜者死......
现在有一个n*n的皇宫,国王占据他所在位置及周围的共9个格子,这些格子皇后不能使用(如果国王位于王宫的边缘,占用的格子可能不到9个)。当然,皇后也不会攻击国王。
现在知道了皇宫的规模n,国王的位置(x,y)(国王位于第x行第y列,行和列号从1开始),请问,有多少种方案放置n个皇后,使她们不能互相攻击(同一横线、竖线、斜线上只能有一个皇后)。
输入说明
输入仅一行,包含三个整数,表示皇宫的规模n(n<=12)及国王的位置x和y坐标。
输出说明
一个整数,表示放置n个皇后的方案数
cpp
#include <stdio.h>
#include <stdlib.h>
#define MAX_N 15
int n, x, y;
int count = 0; // 方案数
int board[MAX_N][MAX_N]; // 棋盘,0表示可放置,1表示皇后,-1表示国王占据
// 检查当前位置是否可以放置皇后
int is_safe(int row, int col) {
// 检查同一列
for (int i = 0; i < row; i++) {
if (board[i][col] == 1) {
return 0;
}
}
// 检查左上对角线
for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
if (board[i][j] == 1) {
return 0;
}
}
// 检查右上对角线
for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
if (board[i][j] == 1) {
return 0;
}
}
return 1;
}
// 深度优先搜索放置皇后
void dfs(int row) {
if (row == n) { // 所有行都放置了皇后
count++;
return;
}
// 尝试在当前行的每一列放置皇后
for (int col = 0; col < n; col++) {
// 如果这个位置不是国王占据的,且可以安全放置
if (board[row][col] != -1 && is_safe(row, col)) {
board[row][col] = 1; // 放置皇后
dfs(row + 1); // 处理下一行
board[row][col] = 0; // 回溯
}
}
}
int main() {
scanf("%d %d %d", &n, &x, &y);
// 调整坐标:题目中行列从1开始,我们转换为从0开始
x--; // 行号-1
y--; // 列号-1
// 初始化棋盘
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
board[i][j] = 0;
}
}
// 标记国王占据的位置(国王所在及周围8个格子)
for (int i = x - 1; i <= x + 1; i++) {
for (int j = y - 1; j <= y + 1; j++) {
if (i >= 0 && i < n && j >= 0 && j < n) {
board[i][j] = -1; // 标记为国王占据
}
}
}
// 开始DFS搜索
dfs(0);
printf("%d\n", count);
return 0;
}
68:FBI树
作者: Turbo
时间限制: 1s
章节: 深度优先搜索
问题描述
我们可以把由"0"和"1"组成的字符串分为三类:全"0"串称为B串,全"1"串称为I串,既含"0"又含"1"的串则称为F串。
FBI树是一种二叉树,它的结点类型也包括F结点,B结点和I结点三种。由一个长度为2N的"01"串S可以构造出一棵FBI树T,递归的构造方法如下:
1)T的根结点对应的内容为S,因此其类型与串S的类型相同;
2)若串S的长度大于1,将串S从中间分开,分为等长的左右子串S1和S2;由左子串S1构造左子树T1,由右子串S2构造右子树T2。
现在给定一个长度为2N的"01"串,请用上述构造方法构造出一棵FBI树,并输出它的后序遍历序列。
输入说明
第一行是一个整数N(0 <= N <= 10),第二行是一个长度为2N的"01"串。
输出说明
包括一行,这一行只包含一个字符串,即FBI树的后序遍历序列。
cpp
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char str[1025]; // 2^10 = 1024,加上结束符
int length;
// 判断字符串类型
char get_type(int left, int right) {
int has_zero = 0, has_one = 0;
for (int i = left; i <= right; i++) {
if (str[i] == '0') {
has_zero = 1;
} else if (str[i] == '1') {
has_one = 1;
}
// 如果既包含0又包含1,可以直接返回'F'
if (has_zero && has_one) {
return 'F';
}
}
if (has_zero) return 'B';
if (has_one) return 'I';
return 'B'; // 实际上不会执行到这里
}
// 递归构建FBI树并后序遍历
void build_and_traverse(int left, int right) {
// 只有一个字符的情况
if (left == right) {
printf("%c", str[left] == '0' ? 'B' : 'I');
return;
}
// 计算中间位置
int mid = (left + right) / 2;
// 后序遍历:左子树 -> 右子树 -> 根
build_and_traverse(left, mid); // 遍历左子树
build_and_traverse(mid + 1, right); // 遍历右子树
// 输出根节点类型
printf("%c", get_type(left, right));
}
int main() {
int N;
scanf("%d", &N);
scanf("%s", str);
length = strlen(str);
// 递归构建并后序遍历
build_and_traverse(0, length - 1);
printf("\n");
return 0;
}
79:无重叠区间
作者: Turbo
时间限制: 1s
章节: 贪心
问题描述
给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。
注意:
可以认为区间的终点总是大于它的起点。
区间 [1,2] 和 [2,3] 的边界相互"接触",但没有相互重叠。
示例 1:
输入: [ [1,2], [2,3], [3,4], [1,3] ]
输出: 1
解释: 移除 [1,3] 后,剩下的区间没有重叠。
示例 2:
输入: [ [1,2], [1,2], [1,2] ]
输出: 2
解释: 你需要移除两个 [1,2] 来使剩下的区间没有重叠。
示例 3:
输入: [ [1,2], [2,3] ]
输出: 0
解释: 你不需要移除任何区间,因为它们已经是无重叠的了。
输入说明
首先输入区间集合的大小n(n<20000),然后输入n行,每行2个整数,表示区间的起始和结束。
输出说明
输出一个整数
cpp
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int start;
int end;
} Interval;
// 比较函数:按结束时间升序排序
int cmp(const void* a, const void* b) {
Interval* interval_a = (Interval*)a;
Interval* interval_b = (Interval*)b;
if (interval_a->end != interval_b->end) {
return interval_a->end - interval_b->end;
}
return interval_a->start - interval_b->start;
}
int main() {
int n;
scanf("%d", &n);
Interval intervals[n];
for (int i = 0; i < n; i++) {
scanf("%d %d", &intervals[i].start, &intervals[i].end);
}
// 按结束时间升序排序
qsort(intervals, n, sizeof(Interval), cmp);
int count = 0; // 选择的不重叠区间数量
int last_end = -1; // 上一个选择的区间的结束时间
for (int i = 0; i < n; i++) {
// 如果当前区间的开始时间 >= 上一个选择区间的结束时间
// 说明不重叠,选择当前区间
if (intervals[i].start >= last_end) {
count++;
last_end = intervals[i].end;
}
// 否则,当前区间与上一个区间重叠,不选择
}
// 需要移除的区间数量 = 总区间数 - 选择的不重叠区间数
printf("%d\n", n - count);
return 0;
}
70:
排列问题
作者: Turbo
时间限制: 1s
章节: 深度优先搜索
问题描述
求一个0~N-1的排列(即每个数只能出现一次),给出限制条件(一张N*N的表,第i行第j列的1或0,表示为j这个数能不能出现在i这个数后面,并保证表中的第i行第i列为0,i和j都从0开始),将这个排列看成一个自然数,求从小到大排序第K个排列。
样例输入
3 2
0 1 1
1 0 0
0 1 0
样例输出
1 0 2
解释:
对于N=3的没有任何限制的排列如下:
第一:0 1 2
第二:0 2 1
第三:1 0 2
第四:1 2 0
第五:2 0 1
第六:2 1 0
根据题目所给的限制条件由于2不能出现在1后面,0不能出现在2后面
第一:0 2 1
第二:1 0 2
第三:2 1 0
输入说明
第一行为N和K,接下来的N行,每行N个数,0表示不能,1表示能
N<=10,K<=500000
输出说明
所求的排列
cpp
#include <stdio.h>
#include <string.h>
int n, k;
int matrix[12][12]; // 限制矩阵
int used[12]; // 标记数字是否已使用
int path[12]; // 当前排列路径
int count = 0; // 计数器
int found = 0; // 是否找到结果
// 深度优先搜索生成排列
void dfs(int depth) {
if (found) return; // 如果已经找到结果,直接返回
if (depth == n) { // 找到一个完整排列
count++;
if (count == k) {
found = 1;
// 输出结果
for (int i = 0; i < n; i++) {
printf("%d", path[i]);
if (i < n - 1) printf(" ");
}
printf("\n");
}
return;
}
for (int i = 0; i < n; i++) {
if (!used[i]) { // 数字i未被使用
// 检查限制条件
if (depth == 0 || matrix[path[depth - 1]][i] == 1) {
used[i] = 1;
path[depth] = i;
dfs(depth + 1);
used[i] = 0;
if (found) return;
}
}
}
}
int main() {
scanf("%d %d", &n, &k);
// 读取限制矩阵
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
scanf("%d", &matrix[i][j]);
}
}
// 初始化
memset(used, 0, sizeof(used));
memset(path, 0, sizeof(path));
// 深度优先搜索
dfs(0);
return 0;
}
90:拉丁正方形
作者: xxx
时间限制: 1s
章节: 深度优先搜索
问题描述
一种正方形的数字编排
1 2 3 4 5
2 1 4 5 3
3 4 5 1 2
4 5 2 3 1
5 3 1 2 4
是一个5*5的拉丁正方形,每个1到5的整数在每行每列都出现且出现一次。
写个程序计算N*N的的拉丁正方形的总数且要求第一行是: 1 2 3 4 5.......N
输入说明
一行包含一个整数N( 2<= N <= 7)
输出说明
只有一行,表示拉丁正方形的个数,且拉丁正方形的第一行为 1 2 3 . . . N.
cpp
#include <stdio.h>
int n;
int count = 0;
int square[8][8]; // 拉丁方,n≤7,索引从1开始
int row_used[8][8]; // row_used[i][num]:第i行是否已使用数字num
int col_used[8][8]; // col_used[j][num]:第j列是否已使用数字num
// 深度优先搜索
void dfs(int row, int col) {
if (row > n) { // 所有行都填完了
count++;
return;
}
if (col > n) { // 当前行填完了,填下一行
dfs(row + 1, 2); // 第1列固定为1,2,3...n
return;
}
// 尝试所有可能的数字
for (int num = 1; num <= n; num++) {
// 检查数字是否可用
if (!row_used[row][num] && !col_used[col][num]) {
// 放置数字
square[row][col] = num;
row_used[row][num] = 1;
col_used[col][num] = 1;
// 递归填充下一个位置
dfs(row, col + 1);
// 回溯
row_used[row][num] = 0;
col_used[col][num] = 0;
}
}
}
int main() {
scanf("%d", &n);
// 初始化第一行固定为1,2,3,...,n
for (int j = 1; j <= n; j++) {
square[1][j] = j;
row_used[1][j] = 1;
col_used[j][j] = 1;
}
// 初始化第一列固定为1,2,3,...,n
for (int i = 2; i <= n; i++) {
square[i][1] = i;
row_used[i][i] = 1;
col_used[1][i] = 1;
}
// 从(2,2)开始搜索
dfs(2, 2);
printf("%d\n", count);
return 0;
}
94:最大值路径
作者: Turbo
时间限制: 1s
章节: 宽度优先搜索
问题描述
有n阶方阵,从矩阵的左下角元素为起点,从行或列(向右或向上)两个方向上移动,直到右上角。求出有多少条路径可以使得经过的元素累加值最大,最大值是多少。
例:以下矩阵的最大值是47,走的路线其中之一是545665466

输入说明
首先输入n,2<=n<=10
然后输入n行n列方阵元素,每个元素都是整数。
输出说明
输出一行,为两个空格分隔的整数,第一个表示最大值路径的条数,第二个表示最大值。
cpp
#include <stdio.h>
#include <stdlib.h>
int n;
int matrix[12][12]; // 存储矩阵
long long dp[12][12]; // dp[i][j]:从起点到(i,j)的最大路径和
long long cnt[12][12]; // cnt[i][j]:从起点到(i,j)的最大路径和的数量
int main() {
scanf("%d", &n);
// 读取矩阵
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
scanf("%d", &matrix[i][j]);
}
}
// 初始化:起点是左下角 (n,1)
dp[n][1] = matrix[n][1];
cnt[n][1] = 1;
// 初始化最左边一列(只能向上走)
for (int i = n-1; i >= 1; i--) {
dp[i][1] = dp[i+1][1] + matrix[i][1];
cnt[i][1] = 1; // 只有一条路径
}
// 初始化最下面一行(只能向右走)
for (int j = 2; j <= n; j++) {
dp[n][j] = dp[n][j-1] + matrix[n][j];
cnt[n][j] = 1; // 只有一条路径
}
// 动态规划计算其他位置
for (int i = n-1; i >= 1; i--) {
for (int j = 2; j <= n; j++) {
// 可以从下面(i+1,j)或左边(i,j-1)过来
long long from_below = dp[i+1][j];
long long from_left = dp[i][j-1];
if (from_below > from_left) {
dp[i][j] = from_below + matrix[i][j];
cnt[i][j] = cnt[i+1][j];
} else if (from_below < from_left) {
dp[i][j] = from_left + matrix[i][j];
cnt[i][j] = cnt[i][j-1];
} else { // 相等
dp[i][j] = from_below + matrix[i][j];
cnt[i][j] = cnt[i+1][j] + cnt[i][j-1];
}
}
}
// 结果是右上角 (1,n)
printf("%lld %lld\n", cnt[1][n], dp[1][n]);
return 0;
}