蓝桥杯2018决赛-第九届决赛-交换次数

_我已经从中二毕业了

题面

标题:交换次数

IT产业人才需求节节攀升。业内巨头百度、阿里巴巴、腾讯(简称BAT)在某海滩进行招聘活动。
招聘部门一字排开。由于是自由抢占席位,三大公司的席位随机交错在一起,形如:
ABABTATT,这使得应聘者十分别扭。
于是,管理部门要求招聘方进行必要的交换位置,使得每个集团的席位都挨在一起。即最后形如:
BBAAATTT 这样的形状,当然,也可能是:
AAABBTTT 等。

现在,假设每次只能交换2个席位,并且知道现在的席位分布,
你的任务是计算:要使每个集团的招聘席位都挨在一起需要至少进行多少次交换动作。

输入是一行n个字符(只含有字母B、A或T),表示现在的席位分布。
输出是一个整数,表示至少交换次数。

比如,输入:
TABTABBTTTT

程序应该输出:
3

再比如,输入:
TTAAABB

程序应该输出:
0

我们约定,输入字符串的长度n 不大于10万

资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms

题解

首页通过题目可以知道,排序的方式有六种,分别是:

  • ABT
  • ATB
  • BAT
  • BTA
  • TAB
  • TBA

上面其实就是 ABT 的六种组合。我们要做的就是将输入的字符串,根据上面的每一种组合,每一个字符交换到正确的位置上。例如输入TABTAB,排序方式为ABT,就需要将两个AA交换到第 0 位和第 1 位,将两个BB交换到第 2 位和第 3 位,最后两个TT就是一定会在倒数第二位和第一位。由此可的,我们只需要根据每一种组合,知道ABT的先后排序顺序,依次交换直到符合条件,六组里最少交换次数的就是最后的答案。

使用对拍程序生成 10 万的数据,能够在本机 1 秒内执行。

代码

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <vector>
#include <map>
#include <fstream>

using namespace std;

string s;
map<char, int> M;
// ABT 出现的组合情况
char per[6][3] = {
  {'A', 'B', 'T'},
  {'A', 'T', 'B'},
  {'B', 'A', 'T'},
  {'B', 'T', 'A'},
  {'T', 'A', 'B'},
  {'T', 'B', 'A'},
};

int minimumSwapCount (char com[]) {
  map<char, int> m1, m2, m3;
  char c1 = com[0], c2 = com[1], c3 = com[2];
  int l = 0;
  int r = M[c1];
  int res = 0;
  for (int i = l; i < r; i++)
    m1[s[i]]++;
  
  l = r;
  r = l + M[c2];
  for (int i = l; i < r; i++)
    m2[s[i]]++;
  
  l = r;
  r = s.length();
  for (int i = l; i < r; i++)
    m3[s[i]]++;
  
  // printf("A = %d, B = %d, T = %d\n", m1['A'], m1['B'], m1['T']);
  // printf("A = %d, B = %d, T = %d\n", m2['A'], m2['B'], m2['T']);
  // printf("A = %d, B = %d, T = %d\n", m3['A'], m3['B'], m3['T']);
  // cout << endl;

  // m1 只能有 c1 存在,c2 和 c3 都需要与 m2, m3 交换
  while (m1[c2]) {
    if (m2[c1]) {
      m1[c1]++;
      m2[c1]--;
      m1[c2]--;
      m2[c2]++;
      res++;
    } else if (m3[c1]) {
      m1[c1]++;
      m3[c1]--;
      m1[c2]--;
      m3[c2]++;
      res++;
    }
  }
  while (m1[c3]) {
    if (m2[c1]) {
      m1[c1]++;
      m2[c1]--;
      m1[c3]--;
      m2[c3]++;
      res++;
    } else if (m3[c1]) {
      m1[c1]++;
      m3[c1]--;
      m1[c3]--;
      m3[c3]++;
      res++;
    }
  }

  // m1 经过与 m2 和 m3 的交换后,m1 里只会保留 c1
  // m2 里可能还有 c2 和 c3 的值,与 m3 交换
  while (m2[c3]) {
    if (m3[c2]) {
      m2[c2]++;
      m3[c2]--;
      m2[c3]--;
      m3[c3]++;
      res++;
    }
  }

  // 经过上面的循环,m1 内只有 c1,m2 内只有 c2,由此 m3 内只会有 c3
  // 返回本次组合的交换次数
  return res;
}

int main () {
  cin >> s;

  // ifstream infile; 
  // infile.open("in.txt");
  // infile >> s;
  
  int n = s.length(), ans = 1 << 21;

  // 求出输入字符串中,A、B 和 T 的出现次数
  for (int i = 0; i < n; i++)
    M[s[i]]++;
  
  // 求每种组合最小的交换次数
  for (int i = 0; i < 6; i++)
    ans = min(ans, minimumSwapCount(per[i]));

  cout << ans << endl;

  return 0;
}

知识点

  • 排列组合;
  • 交换。
阅读 2k

各种各样的学习笔记
不搞前端会死星人!

不搞前端会死星人。

7.9k 声望
235 粉丝
0 条评论

不搞前端会死星人。

7.9k 声望
235 粉丝
文章目录
宣传栏