题目描述
MasterMind\texttt{MasterMind}MasterMind 是一个双人游戏。其中一方是设计者,选择一组秘密代码。另一方是破解者,试图破解它。代码只是一行彩色点。在游戏开始时,玩家约定代码的长度 NNN 以及代码中可能出现的颜色。
为了破解代码,破解者进行多次猜测,每次猜测本身也是一个代码。每次猜测后,设计者给出一个提示,说明猜测与秘密代码的匹配程度。
在本题中,给定一个秘密代码 s1,s2,…,sns_1, s_2, \dots, s_ns1,s2,…,sn 和一个猜测代码 g1,g2,…,gng_1, g_2, \dots, g_ng1,g2,…,gn,需要确定提示。提示由一对数字确定:
- 强匹配:si=gjs_i = g_jsi=gj 且 i=ji = ji=j
- 弱匹配:si=gjs_i = g_jsi=gj 且 i≠ji \neq ji=j
设计者选择一组独立匹配(即每个位置最多被使用一次),使得强匹配和弱匹配的数量都最大。提示由强匹配数后跟弱匹配数组成。
如果提示是 (n,0)(n,0)(n,0),则猜测与秘密代码完全相同。
输入格式
输入包含多场游戏的数据。每场游戏以一个整数 NNN(代码长度)开始,然后是秘密代码(NNN 个整数,范围 111 到 999)。接着是任意数量的猜测,每个猜测也是 NNN 个整数(范围 111 到 999)。每场游戏的最后一个猜测后跟 NNN 个 000(这些 000 不应视为猜测)。
第一场游戏之后是第二场游戏的数据(以新的 NNN 开始)。输入的最后一场游戏后跟一个单独的 000(即 N=0N=0N=0)。NNN 的最大值为 100010001000。
输出格式
对于每场游戏,输出游戏编号,然后按顺序为每个猜测输出一行提示。每个提示表示为 (strong,weak)。格式如样例所示。
样例输入
4
1 3 5 5
1 1 2 3
4 3 3 5
6 5 5 1
6 1 3 5
1 3 5 5
0 0 0 0
10
1 2 2 2 4 5 6 6 6 9
1 2 3 4 5 6 7 8 9 1
1 1 2 2 3 3 4 4 5 5
1 2 1 3 1 5 1 6 1 9
1 2 2 5 5 5 6 6 6 7
0 0 0 0 0 0 0 0 0 0
0
样例输出
Game 1:
(1,1)
(2,0)
(1,2)
(1,2)
(4,0)
Game 2:
(2,4)
(3,2)
(5,0)
(7,0)
题目分析
问题的本质
这是一个匹配计数问题。给定两个等长的序列 sss 和 ggg,需要计算:
- 强匹配数:相同位置上数值相等的个数
- 弱匹配数:不同位置上数值相等的个数(每个数值最多被计数一次)
算法描述
计算强匹配很简单:直接遍历所有位置,统计 secret[i] == guess[i] 的数量。
对于弱匹配,需要处理剩余的数字(排除已匹配的强匹配位置)。对于每个数值 vvv,它在秘密代码中剩余的个数为 count_secret[v],在猜测代码中剩余的个数为 count_guess[v]。弱匹配数为所有 vvv 的 min(count_secret[v], count_guess[v]) 之和。
示例说明
以第一个示例的第一组数据为例:
- 秘密代码:
1 3 5 5 - 猜测代码:
1 1 2 3
强匹配:位置 111 都是 111,所以 strong = 1
剩余数字:
- 秘密剩余:
3, 5, 5 - 猜测剩余:
1, 2, 3
统计频率:
| 数值 | 秘密剩余次数 | 猜测剩余次数 | min |
|---|---|---|---|
| 1 | 0 | 1 | 0 |
| 2 | 0 | 1 | 0 |
| 3 | 1 | 1 | 1 |
| 5 | 2 | 0 | 0 |
weak = 1
提示:(1,1)
参考代码
// Master-Mind Hints
// UVa ID: 340
// Verdict: Accepted
// Submission Date: 2016-06-27
// UVa Run Time: 0.000s
//
// 版权所有(C)2016,邱秋。metaphysis # yeah dot net
#include <bits/stdc++.h>
using namespace std;
int main(int argc, char *argv[])
{
int secret[1100], guess[1100], n, cases = 0;
while (cin >> n, n)
{
// 读取秘密代码
for (int i = 1; i <= n; i++)
cin >> secret[i];
cout << "Game " << ++cases << ":" << endl;
// 处理猜测
while (true)
{
int strong = 0;
// 统计强匹配,同时收集未匹配的数字频率
map<int, int> S, G;
for (int i = 1; i <= n; i++)
{
cin >> guess[i];
if (secret[i] == guess[i])
strong++;
else
{
S[secret[i]]++;
G[guess[i]]++;
}
}
// 猜测结束标志:第一个数字为 0
if (guess[1] == 0)
break;
// 计算弱匹配
int weak = 0;
for (auto& p : S)
if (G.find(p.first) != G.end())
weak += min(p.second, G[p.first]);
cout << " (" << strong << "," << weak << ")" << endl;
}
}
return 0;
}

402

被折叠的 条评论
为什么被折叠?



