UVA-1609 不公平竞赛 题解答案代码 算法竞赛入门经典第二版

GitHub - jzplp/aoapc-UVA-Answer: 算法竞赛入门经典 例题和习题答案 刘汝佳 第二版

题目实际上就是:这个队伍能打过超过一半的队伍,且没有绝对的胜者。要求找一个合适的安排使这个队伍可以最终获胜。

方法采用算法竞赛入门经典书中的方法,如果让我自己想可能比较困难或者想不出来。设1号队伍可以打败的队伍为白色队伍,不可以直接打败的为黑色队伍。方法分为四步:

  1. 消灭可以直接消灭的黑色队伍,进行比赛。

  2. 对1号队伍选一个可以肯定打败的队伍,进行比赛。

  3. 所有剩下的黑色队伍互相比赛。

  4. 所有剩下的其余队伍互相比赛。注意步骤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;
}
相关推荐
地平线开发者25 分钟前
CLIP 的训练机制:从对比学习到图文对齐
深度学习·算法·自动驾驶
PaperAccept1 小时前
【研知有术论文发表】材料科学一区期刊,影响因子30+的纳米科技SCI,审稿严格但有效率
大数据·数据结构·科技·量子计算
hetao17338371 小时前
2026-11-02 hetao1733837 的刷题记录
c++·算法
Geektec1 小时前
IMU 选型完全指南:从硬件参数到算法融合的系统工程视角
算法·imu·陀螺仪·加速度计·元器件选型
CQU_JIAKE1 小时前
11.2【A】
算法
Tairitsu_H1 小时前
[C++高阶数据结构·图论下] 最短路径:为什么“挑最小的”一定正确?
数据结构·c++·图论
Godspeed Zhao2 小时前
从零开始学AI21——决策树
算法·决策树·机器学习
开开心心就好2 小时前
网页图片批量下载工具,输入链接就保存
服务器·算法·leetcode·支持向量机·pdf·ocr·1024程序员节
Tairitsu_H2 小时前
[C++高阶数据结构·图论上] 最小生成树:为什么“选边”和“选点”是两条路?
数据结构·c++·图论·最小生成树
starzy19903 小时前
Scala List操作:不可变链表的函数式美学
链表·list·scala