P1199 [NOIP 2010 普及组] 三国游戏
题目描述
小涵很喜欢电脑游戏,这些天他正在玩一个叫做《三国》的游戏。
在游戏中,小涵和计算机各执一方,组建各自的军队进行对战。游戏中共有 NNN 位武将(NNN 为偶数且不小于 444),任意两个武将之间有一个“默契值”,表示若此两位武将作为一对组合作战时,该组合的威力有多大。游戏开始前,所有武将都是自由的(称为自由武将,一旦某个自由武将被选中作为某方军队的一员,那么他就不再是自由武将了),换句话说,所谓的自由武将不属于任何一方。
游戏开始,小涵和计算机要从自由武将中挑选武将组成自己的军队,规则如下:小涵先从自由武将中选出一个加入自己的军队,然后计算机也从自由武将中选出一个加入计算机方的军队。接下来一直按照“小涵 →\to→ 计算机 →\to→ 小涵 →…\to\dots→… ”的顺序选择武将,直到所有的武将被双方均分完。然后,程序自动从双方军队中各挑出一对默契值最高的武将组合代表自己的军队进行二对二比武,拥有更高默契值的一对武将组合获胜,表示两军交战,拥有获胜武将组合的一方获胜。
已知计算机一方选择武将的原则是尽量破坏对手下一步将形成的最强组合,它采取的具体策略如下:任何时刻,轮到计算机挑选时,它会尝试将对手军队中的每个武将与当前每个自由武将进行一一配对,找出所有配对中默契值最高的那对武将组合,并将该组合中的自由武将选入自己的军队。 下面举例说明计算机的选将策略,例如,游戏中一共有666个武将,他们相互之间的默契值如下表所示:
武将编号 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
1 | 555 | 282828 | 161616 | 292929 | 272727 | |
2 | 555 | 232323 | 333 | 202020 | 111 | |
3 | 282828 | 232323 | 888 | 323232 | 262626 | |
4 | 161616 | 333 | 888 | 333333 | 111111 | |
5 | 292929 | 202020 | 323232 | 333333 | 121212 | |
6 | 272727 | 111 | 262626 | 111111 | 121212 |
双方选将过程如下所示:
小涵 | 轮到计算机时可选的自由武将 | 计算机 | 计算机选将说明 | |
---|---|---|---|---|
第一轮 | 555 | 1,2,3,4,61,2,3,4,61,2,3,4,6 | 4\color{magenta}44 | 小涵手中的 555 号武将与 444 号的默契值最高,所以计算机选择 444 号。 |
第二轮 | 5,35,35,3 | 1,2,61,2,61,2,6 | 4,14,\color{magenta}14,1 | 小涵手中的 555 号和 333 号武将与自由武将中配对可产生的最大默契值为 292929,是由 555 号与 111 号配对产生的,所以计算机选择 111 号。 |
第三轮 | 5,3,65,3,65,3,6 | 222 | 4,1,24,1,\color{magenta}24,1,2 |
小涵想知道,如果计算机在一局游戏中始终坚持上面这个策略,那么自己有没有可能必胜?如果有,在所有可能的胜利结局中,自己那对用于比武的武将组合的默契值最大是多少?
假设整个游戏过程中,对战双方任何时候均能看到自由武将队中的武将和对方军队的武将。为了简化问题,保证对于不同的武将组合,其默契值均不相同。
输入格式
共 NNN 行。
第一行为一个偶数 NNN,表示武将的个数。
第 222 行到第 NNN 行里,第 i+1i+1i+1 行有 N−iN-iN−i 个非负整数,每两个数之间用一个空格隔开,表示 iii 号武将和 $i+1,i+2,\dots,N $ 号武将之间的默契值(0≤默契值≤1090≤\text{默契值}\le10^90≤默契值≤109)。
输出格式
共一或二行。
若对于给定的游戏输入,存在可以让小涵获胜的选将顺序,则输出 $ 1$,并另起一行输出所有获胜的情况中,小涵最终选出的武将组合的最大默契值。如果不存在可以让小涵获胜的选将顺序,则输出 000。
输入输出样例 #1
输入 #1
6
5 28 16 29 27
23 3 20 1
8 32 26
33 11
12
输出 #1
1
32
输入输出样例 #2
输入 #2
8
42 24 10 29 27 12 58
31 8 16 26 80 6
25 3 36 11 5
33 20 17 13
15 77 9
4 50
19
输出 #2
1
77
说明/提示
数据范围
对于 $ 40%$ 的数据有 N≤10N≤10N≤10。
对于 $ 70%$ 的数据有 $ N≤18$。
对于 100%100\%100% 的数据有 4≤N≤5004\le N≤5004≤N≤500。保证对于不同的武将组合,其默契值均不相同。
NOIP2010 普及组 第四题
为了解决这个问题,我们需要确保小涵在计算机的干扰策略下,仍能找到一个可以确保胜利的武将组合,并找出其中最大的默契值。
方法思路
- 问题分析:计算机的策略是每次选择破坏小涵可能形成的最强组合。因此,小涵无法获得任何一个武将的最大默契值组合,但可以确保获得次大值组合中的最大者。
- 关键思路:对于每个武将,找到与其搭配的所有默契值中的次大值,然后在这些次大值中找出最大的那个。这个值即为小涵能确保的最大默契值。
- 实现步骤:
- 读取输入并构建一个完整的默契值矩阵。
- 对于每个武将,收集所有与其相关的默契值,排序后取次大值。
- 在所有次大值中找到最大值,输出结果。
解决代码
#include <iostream>
#include <algorithm>
using namespace std;
const int MAX_N = 505; // 最大武将数+5防止越界
int mat[MAX_N][MAX_N] = {0}; // 存储武将间的默契值矩阵
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; ++i) {
for (int j = i + 1; j <= n; ++j) { // 第i行对应i号武将的后续武将
int val;
cin >> val;
mat[i][j] = mat[j][i] = val; // 对称赋值,保证i-j和j-i的默契值一致
}
}
int max_second = 0; // 保存所有次大值中的最大值
// 遍历每个武将,计算其搭档的次大默契值
for (int i = 1; i <= n; ++i) {
int vals[MAX_N] = {0}; // 存储当前武将的所有默契值
int cnt = 0;
// 收集当前武将i与其他所有武将的默契值
for (int j = 1; j <= n; ++j) {
if (j != i) { // 不包括自己
vals[cnt++] = mat[i][j];
}
}
// 降序排序:先按从大到小排序,取第1个元素(次大值)
sort(vals, vals + cnt, greater<int>());
// 至少有两个值才能取次大值
if (cnt >= 2) {
if (vals[1] > max_second) {
max_second = vals[1];
}
}
}
// 根据题意,总能找到策略使小涵获胜,输出1和最大次大值
cout << "1\n" << max_second << endl;
return 0;
}
代码解释
- 输入处理:读取武将数量n,并构建一个n+1 x n+1的矩阵来存储武将之间的默契值。
- 构建矩阵:通过两层循环读取输入数据,并填充到矩阵中,确保每个武将对的默契值被正确记录。
- 计算次大值:对于每个武将,收集其所有可能的默契值,排序后取次大值,并更新全局最大值。
- 输出结果:根据题目要求,输出1和最大的次大默契值,确保小涵可以获胜。
这种方法通过直接分析每个武将的次大默契值,确保在计算机的干扰策略下,小涵仍能获得最大的可能胜利组合,保证了算法的正确性和效率。