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;
}
相关推荐
学习星球38 分钟前
贪心算法精讲——贪心策略的“局部最优“如何通向全局最优?
算法·贪心算法
MuMuMu122340 分钟前
文旅户外场景智能回收终端工程难点解析:越华环保集团碳惠小屋耐候与供电系统实践
java·大数据·算法
hzxpaipai42 分钟前
制造业官网产品信息架构怎么设计?从产品分类到后台数据结构
大数据·数据结构
玖玥拾1 小时前
LeetCode 205 同构字符串
算法·leetcode
影视飓风TIM1 小时前
C++哈希表:unordered_set / unordered_map底层原理
c++·算法·哈希算法·散列表
专注API从业者1 小时前
Open‑Claw 实战|无需逆向,快速搭建电商商品监控与数据分析系统
开发语言·数据结构·数据库·数据分析·php
在所不辞兄1 小时前
【零基础学智能仿真-06】决策树——让模型自动发现力学失效阈值
算法·决策树·机器学习
啥都想学点的研究生1 小时前
一篇文章讲清楚:C4.5决策树和CART决策树
算法·决策树·机器学习