例题 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;
}