GitHub - jzplp/aoapc-UVA-Answer: 算法竞赛入门经典 例题和习题答案 刘汝佳 第二版
题目实际上就是:这个队伍能打过超过一半的队伍,且没有绝对的胜者。要求找一个合适的安排使这个队伍可以最终获胜。
方法采用算法竞赛入门经典书中的方法,如果让我自己想可能比较困难或者想不出来。设1号队伍可以打败的队伍为白色队伍,不可以直接打败的为黑色队伍。方法分为四步:
-
消灭可以直接消灭的黑色队伍,进行比赛。
-
对1号队伍选一个可以肯定打败的队伍,进行比赛。
-
所有剩下的黑色队伍互相比赛。
-
所有剩下的其余队伍互相比赛。注意步骤3中可能剩下一个黑色队伍没有参加过,此时要加进这里来比赛。
每轮比赛时取出一般的队伍,这样O(logn)的轮次就能找到第一名。
但有了方法还是超时。
一开始我完全使用数组实现,使用两个标记数组表示剩下的队伍,与本轮未参加过还剩下的队伍。每次遍历整个队伍来查找。这样一个轮次需要n^2,整个需要O(n^2logn), 超时。
我发现遍历本轮次剩下的队伍时,没有使用随机存取,而是仅使用顺序查找。这样把本轮没有参加过的队伍用一个链表表示,由于每轮这个链表长度都减小一半,因此花费时间依次为:
n^2 + (n/2)^2 + (n/4)^2 + ... = n^2 + n^2/4 + n^2/16 + ... = O(n^2)
虽然时间减少了很多,但还是超时。
然后我发现,黑色队伍和白色队伍在方法中一般都是分开统计的,因此分为两个链表单独计算。这样就没有遍历整个长度的机会。因此花费时间依次为:
(n/2)^2 + + (n/4)^2 + ... = n^2/4 + n^2/16 + ... = O(n^2)
虽然还是O(n^2),但实际上去掉了最前面的运算,且最终只需要第一步是n^2的计算量。时间消耗明显减少。最终两个链表的方法AC了。
AC代码
cpp
#include <stdio.h>
#include <string.h>
#include <list>
#define MAXN 1030
using namespace std;
int group[MAXN][MAXN];
int used[MAXN];
int n;
list<int> lsb, lsw;
int history[MAXN * MAXN][2];
int hisn;
bool judge()
{
int i;
for (i = 2; i <= n; ++i)
if (!used[i])
return false;
return true;
}
void beat(list<int> &l1, list<int>::iterator &it, list<int> &l2, list<int>::iterator &it2)
{
if (group[*it][*it2])
used[*it2] = 1;
else
used[*it] = 1;
history[hisn][0] = *it;
history[hisn][1] = *it2;
++hisn;
it = l1.erase(it);
// 避免删除it2后,it也不存在了
if (l1 == l2 && it == it2)
{
it2 = l2.erase(it2);
it = it2;
}
else
{
it2 = l2.erase(it2);
}
}
void computed()
{
list<int>::iterator it3;
bool flag;
// 第一步消灭所有直接消灭的黑色
for (auto it = lsb.begin(); it != lsb.end();)
{
flag = false;
for (auto it2 = lsw.begin(); it2 != lsw.end();)
{
if (group[*it][*it2])
{
++it2;
continue;
}
beat(lsb, it, lsw, it2);
flag = true;
break;
}
if (!flag)
++it;
}
// 第二步 1和另一个
if (lsw.size() > 0)
{
auto it = lsw.begin();
used[*it] = 1;
history[hisn][0] = 1;
history[hisn][1] = *it;
++hisn;
it = lsw.erase(it);
}
// 第三步 黑黑对决
for (auto it = lsb.begin(); it != lsb.end();)
{
it3 = it;
if (++it != lsb.end())
beat(lsb, it3, lsb, it);
}
// 第四步 剩下混战
for (auto it = lsw.begin(); it != lsw.end();)
{
it3 = it;
if (++it != lsw.end())
beat(lsw, it3, lsw, it);
}
if (lsw.size() > 0 && lsb.size() > 0)
{
auto it = lsb.begin(), it2 = lsw.begin();
beat(lsw, it, lsb, it2);
}
}
int main()
{
int i, j;
char c;
while (scanf("%d", &n) > 0)
{
for (i = 1; i <= n; ++i)
{
getchar();
for (j = 1; j <= n; ++j)
{
scanf("%c", &c);
group[i][j] = c - '0';
}
}
memset(used, 0, sizeof(used));
memset(history, 0, sizeof(history));
hisn = 0;
while (1)
{
lsb.clear();
lsw.clear();
for (i = 2; i <= n; ++i)
{
if (used[i])
continue;
if (group[1][i])
lsw.push_back(i);
else
lsb.push_back(i);
}
computed();
if (judge())
break;
}
for (i = 0; i < hisn; ++i)
{
printf("%d %d\n", history[i][0], history[i][1]);
}
// putchar('\n');
}
return 0;
}