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;
}
相关推荐
倒头就睡的小比特1 天前
算法竞赛C++常用的STL
c++·算法
小羊没烦恼!1 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
猎头南楼1 天前
知识社区推荐系统实践:新用户冷启动与长短期兴趣建模的挑战 资深推荐算法工程师
人工智能·深度学习·算法·机器学习
m0_547486661 天前
《数据结构教程》全套 PPT课件2026
数据结构
旖旎夜光1 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
wzdark1 天前
大规模并行计算中的负载均衡算法研究4
算法
Because_of_Her11 天前
并查集-听课笔记
笔记·算法·并查集
码流子1 天前
高速公路安全监测实践:碰撞监测预警+物联网底座,从感知到处置的闭环
大数据·人工智能·物联网·算法·架构
another heaven1 天前
【算法/C++ MD5算法能否逆解码?原理、C++实现与同类哈希算法对比】
c++·算法·哈希算法
wzdark1 天前
从算法设计模式看编程思维的抽象能力4
算法