第四章 函数与递归

例题 4-1 古老的密码 (Ancient Cipher, NEERC 2004,UVa1339)

给定两个长度相同且不超过100 的字符串,判断是否能把其中一个字符串的各个字母重排,然后对26个字母做一个一一映射,使得两个字符串相同。例如,JWPUDJSTVP重排后可以得到 WJDUPSJPVT,然后把每个字母映射到它前一个字母(B->A,C->B,...,Z->Y,A->Z),得到VICTORIOUS。输入两个字符串,输出YES 或者NO。

解:比较两个字符串的字母频率分布。将两个字符串的字母频率按降序排序后,若两者一致,则可通过重排和映射使字符串相等。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

bool is_transform(const string s1,const string s2){
	int s1_sz[26] = {0};
	int s2_sz[26] = {0};
	
	for(char c : s1) s1_sz[c-'A']++;
	for(char c : s2) s2_sz[c-'A']++;
	
	sort(s1_sz,s1_sz + 26);
	sort(s2_sz,s2_sz + 26);
	
	for(int i = 0;i <= 26;i++){
		if(s1_sz[i] != s2_sz[i]) return false;
	}
	return true;
}

int main(){
	//freopen("in.txt","r",stdin);
	string s1,s2;
	cin >> s1 >> s2;
	if(is_transform) cout << "YSE" << endl;
	else cout<< "NO" << endl;
    return 0;
}

例题4-3 黑白棋(Othello, ACM/ICPC World Finals 1992,UVa220)

你的任务是模拟黑白棋游戏的进程。黑白棋的规则为:黑白双方轮流放棋子,每次必须让新放的棋子"夹住"至少一枚对方棋子,然后把所有被新放棋子"夹住"的对方棋子替换成已方棋子。一段连续(横、竖或者斜向)的同色棋子被"夹住"的条件是两端都是对方棋子(不能是空位)。如图4-6(a)所示,白棋有6个合法操作,分别为(2,3),(3,3),(3,5)、(6.2).(7,3),(7,4)。选择在(7,3)放白棋后变成如图4-6(b)所示效果(注意有竖向和斜向的共两枚黑棋变白)。注意(4,6)的黑色棋子虽然被夹住,但不是被新放的棋子夹住,因此不变白。

|-----------------------------------------------------------------------------|-----------------------------------------------------------------------------|
| | |

输入一个8*8的棋盘以及当前下一次操作的游戏者,处理3种指令:

L指令打印所有合法操作,按照从上到下,从左到右的顺序排列(没有合法操作时输出 No legal move)。

Mrc指令放一枚棋子在(r,c)。如果当前游戏者没有合法操作,则是先切换游戏者再操作。输入保证这个操作是合法的。输出操作完毕后黑白方的棋子总数。

Q指令退出游戏,并:打印当前棋盘(格式同输入)。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

//0  0  0  0  0  0  0  0
//0  0  0  0  0  0  0  0
//0  0  0 -1  0  0  1  0
//0  0  0 -1 -1 -1  0  0
//0  0  1 -1  1  1  0  0
//0  0 -1 -1 -1  1  0  0
//0  0  0  0  0  0  0  0
//0  0  0  0  0  0  0  0

int board[9][9] = {0}; // 0 表示无棋子; -1表示黑棋; 1表示白棋 
int player = 1; // 初始玩家为白棋(1)
int directions[8][2] = {{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}};//八个方向 

void cin_board() { // 输入棋盘 
    for (int i = 1; i <= 8; i++) {
        for (int j = 1; j <= 8; j++) {
            cin >> board[i][j];
        }
    }
}

void cout_board() { // 输出棋盘 
    cout << "  ";
    for (int j = 1; j <= 8; j++) cout << j << " ";
    cout << endl;
    
    for (int i = 1; i <= 8; i++) {
        cout << i << " ";
        for (int j = 1; j <= 8; j++) {
            if (board[i][j] == 0) cout << ". ";
            else if (board[i][j] == 1) cout << "W ";
            else cout << "B ";
        }
        cout << endl;
    }
}

bool check_direction(int r, int c, int dr, int dc, int player) {// 检查某个方向是否可以翻转棋子
    int opponent = -player;//对方棋子 
    int x = r + dr;
    int y = c + dc;
    bool found_opponent = false;//是否存在对方棋子 
    
    while (x >= 1 && x <= 8 && y >= 1 && y <= 8) {//限制在棋盘内 
        if (board[x][y] == opponent) {//找到对方棋子,沿此方向继续 
            found_opponent = true;
            x += dr;
            y += dc;
        } else if (board[x][y] == player && found_opponent) {//路径上有对方棋子且 遇到我方棋子 
            return true;
        } else {
            break;
        }
    }
    return false;
}

bool is_valid_move(int r, int c, int player) {// 判断是否为合法移动
    if (r < 1 || r > 8 || c < 1 || c > 8) return false;
    if (board[r][c] != 0) return false;//此位置有棋子,返回非法 
    
    for (int i = 0; i < 8; i++) {
        if (check_direction(r, c, directions[i][0], directions[i][1], player)) {
            return true;
        }
    }
    return false;
}

void flip_pieces(int r, int c, int player) {// 执行翻转操作
    int opponent = -player;
    board[r][c] = player;
    
    for (int i = 0; i < 8; i++) {
        int dr = directions[i][0];
        int dc = directions[i][1];
        int x = r + dr;
        int y = c + dc;
        
        // 检查这个方向是否有可以翻转的棋子
        bool can_flip = false;
        while (x >= 1 && x <= 8 && y >= 1 && y <= 8 && board[x][y] == opponent) {
            x += dr;
            y += dc;
            if (x >= 1 && x <= 8 && y >= 1 && y <= 8 && board[x][y] == player) {
                can_flip = true;
                break;
            }
        }
        
        // 如果可以翻转,则进行翻转
        if (can_flip) {
            x = r + dr;
            y = c + dc;
            while (board[x][y] == opponent) {
                board[x][y] = player;
                x += dr;
                y += dc;
            }
        }
    }
}

vector<pair<int, int>> get_legal_moves(int player) {// 获取所有合法移动
    vector<pair<int, int>> moves;
    for (int r = 1; r <= 8; r++) {
        for (int c = 1; c <= 8; c++) {
            if (is_valid_move(r, c, player)) {
                moves.push_back({r, c});
            }
        }
    }
    return moves;
}

void show_score() {//计分 
    int W = 0, B = 0;
    for (int i = 1; i <= 8; i++) {
        for (int j = 1; j <= 8; j++) {
            if (board[i][j] == 1) W++;
            else if (board[i][j] == -1) B++;
        }
    }
    cout << "当前白棋数量为:" << W << " 当前黑棋数量为:" << B << endl;
}

void process_command(const string& command) { // 处理指令 
    if (command.empty()) return;
    char cmd = command[0];
    
    switch(cmd) {
        case 'L': { // 查找合法操作 
            auto moves = get_legal_moves(player);
            if (moves.empty()) {
                cout << "当前玩家没有合法移动,跳过回合" << endl;
                player = -player; // 切换到对方
                
                // 检查对手是否有合法移动
                auto opponent_moves = get_legal_moves(player);
                if (opponent_moves.empty()) {
                    cout << "双方都没有合法移动,游戏结束!" << endl;
                    show_score();
                    exit(0);
                }
                return;
            }
            
            cout << "合法移动位置: ";
            for (const auto& move : moves) {
                cout << "(" << move.first << "," << move.second << ") ";
            }
            cout << endl;
            break;
        }
        
        case 'M': { // 放置棋子 
            if (command.length() < 3) {
                cout << "输入格式错误,应为Mrc(如M34表示在第3行第4列落子)" << endl;
                return;
            }
            
            int r = command[1] - '0';
            int c = command[2] - '0';
            
            if (r < 1 || r > 8 || c < 1 || c > 8) {
                cout << "坐标超出范围,应为1-8" << endl;
                return;
            }
            
            if (!is_valid_move(r, c, player)) {
                cout << "非法移动!请使用L命令查看合法位置" << endl;
                return;
            }
            
            cout << "玩家 " << (player == 1 ? "白棋(W)" : "黑棋(B)") 
                 << " 在 (" << r << "," << c << ") 落子" << endl;
            
            flip_pieces(r, c, player);
            cout_board();
            show_score();
            
            // 切换到对手
            player = -player;
            
            // 检查对手是否有合法移动
            auto opponent_moves = get_legal_moves(player);
            if (opponent_moves.empty()) {
                cout << "对手没有合法移动,继续当前玩家回合" << endl;
                player = -player; // 换回原玩家
                
                // 检查原玩家是否有合法移动
                auto current_moves = get_legal_moves(player);
                if (current_moves.empty()) {
                    cout << "双方都没有合法移动,游戏结束!" << endl;
                    show_score();
                    exit(0);
                }
            }
            break;
        }
        
        case 'Q': {// 退出游戏 
            cout << "最终棋盘状态:" << endl;
            cout_board(); 
            show_score();
            cout << "Game end." << endl;
            exit(0);
            break;
        }
        
        default:
            cout << "输入错误!" << endl;
            break; 
    }
}

int main() {
    cin_board(); 
    cout << "\n初始棋盘状态:" << endl;
    cout_board();
    show_score();
    
    string command;
    while (1) {
        cout << "\n========================================" << endl;
        cout << "L: 查找所有合法操作" << endl;
        cout << "Mrc: 在第r行c列放置棋子(如M34)" << endl;
        cout << "Q: 退出游戏" << endl;
        cout << "当前玩家: " << (player == 1 ? "白棋(W)" : "黑棋(B)") << endl;
        cout << "请输入操作: ";
        
        cin >> command;
        process_command(command);
    }
    return 0;
}

例题 4-4 骰子涂色 (Cube painting,UVa 253)

我用键值对分别存储A、B的1-6 2-5 3-4,用A的123与B的123456比较。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

//rbgggrrggbgr
//rrrbbbrrbbbr
//rbgrbgrrrrrg

int main() {
    pair <char,char> pairA[4];
	pair <char,char> pairB[4];
	
	while(cin >> pairA[1].first >> pairA[2].first >> pairA[3].first >> pairA[3].second >> pairA[2].second >> pairA[1].second
	    >> pairB[1].first >> pairB[2].first >> pairB[3].first >> pairB[3].second >> pairB[2].second >> pairB[1].second){
	    	bool find_B[4] = {0};
			bool xiangdeng = true;
			for(int i = 1;i<=3;i++){
				bool find = false;//对第j轮的B查找失败 
				for(int j = 1;j<=3;j++){
					if(find_B[j]) continue;
					if(pairA[i].first == pairB[j].first && pairA[i].second == pairB[j].second 
					|| pairA[i].first == pairB[j].second && pairA[i].second == pairB[j].first){
						find_B[j] = true;//标记B[j]已被查询 
						find = true; 
					}
				}
				if(!find) xiangdeng = false;
			}
			if(xiangdeng) cout << "TRUE" << endl;
			else cout << "FALSE" << endl;
		}
    return 0;
}

例题 4-10 洪水! (Flooded!ACM/CPC World Finals 1999,UVa 253)

按照格子高低先后进行淹没,看为填格子。设定海平线double X,用一维数组high[]存储每个格子的海拔高度,sort排序一下,用int V存雨水总体积。然后V/100即为需要装的水格子的高度,利用海平线X与第k个格子的关系,X = (H + sum(k))/(k+1) "sum(k)为数组high的前k项和"用for循环遍历k从0开始,终止条件为X < high[k+1]即海平面低于第high[k+1]海拔高度。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

//3 3
//25 37 45
//51 12 34
//94 83 27
//10000


int n,m,V;
int high[10] = {0};

int sum(int k){
	int T = 0;
	for(int i = 0;i<=k;i++){T += high[i];}
	return T;
}

int main() {
	cin >> n >> m;
	for(int i = 0;i<n*m;i++){
		cin >> high[i];
	} 
	cin >> V;
	sort(high,high+n*m);
	double H = V/100.0;
	for(int k=0;;k++){
		double X = (H + sum(k))/(k+1);
		if(X < high[k+1]) {
			cout << fixed << setprecision(2) << "海拔高度为: " <<  X << "米" << endl;
			double out = 100.0*(k+1)/(n*m);
			cout << fixed << setprecision(2) << "淹没区域百分比为: " <<  out << "%" << endl;
			break;
		}
	}
    return 0;
}
相关推荐
漫随流水2 小时前
leetcode回溯算法(77.组合)
数据结构·算法·leetcode·回溯算法
玄冥剑尊2 小时前
动态规划入门
算法·动态规划·代理模式
mjhcsp2 小时前
P14987 全等(mjhcsp)
算法·题解·洛谷
(❁´◡`❁)Jimmy(❁´◡`❁)2 小时前
Atcoder abc441A~F 题解
算法·深度优先·图论
少林码僧3 小时前
2.30 传统行业预测神器:为什么GBDT系列算法在企业中最受欢迎
开发语言·人工智能·算法·机器学习·ai·数据分析
豆沙沙包?3 小时前
2026年--Lc343-1926. 迷宫中离入口最近的出口(图 - 广度优先搜索)--java版
java·算法·宽度优先
超级大福宝3 小时前
【力扣200. 岛屿数量】的一种错误解法(BFS)
数据结构·c++·算法·leetcode·广度优先
独自破碎E3 小时前
【动态规划=递归+记忆化存储】跳台阶
算法·动态规划
一颗青果3 小时前
auto | 尾置返回类型 | decltype | using | typedef
java·开发语言·算法