P8672 [蓝桥杯 2018 国 C] 交换次数 题解

· · 题解

1. 编程思路。

题目中的字母有 B、T、A 这 3 种,3 种字母有 6 种排列方式(BAT、BTA、ABT、ATB、TAB、TBA),对每种排列方式求出其最少的交换次数,取 6 个最少交换次数的最小值就是所求的答案。 下面我们来讨论将只包含 ABC 三种字母的字符串 SABC 进行排列需要的最少交换次数。

先统计出字符串 SABC 三种字母各自的个数,不妨分别记为 acntbcntccnt。字符串 S 按要求排列好后,应该分成 ABC 三段(或三个区域),其中第 1 段应该是 acntA,第 2 段应该是 bcntB,第 3 段应该是 ccntC

我们先考察第 1 段,这一段应该全部放字母 A。第 1 段不是字母 A 的字符肯定要换到后面的两段中去,其个数记为 f1t23(表示需要从第 1 段换到后面的第 23 两段的非 A 字母个数)。但是这个不是 A 的字符究竟换到后面的哪一段还是有讲究的,如果是字母 B,显然换到第 2 段更好些,为此也统计出第 1 段中优先换到第 2 段的字母 B 的个数,记为 f1t2

再考察第 2 段,这一段应该放 bcnt 个字母 B。不是字母 B 的字符 A 肯定要换到第 1 段,其个数记为 f2t1,字符 C 肯定要换到第 3 段,其个数记为 f2t3

2 段全部排好了,剩下的第 3 段肯定会排好,就不再考察了。

在前面的考察中,若 f1t2= f2t1,也就是说第 1 段正好有 xB 需要换到第 2 段,而第 2 段中正好有 xA 需要换到第 1 段,则它们直接交换肯定皆大欢喜,交换次数也最少。这种情况下,需要的最少交换次数为 f1t23+f2t3。注意:f1t23 次数中包括 f1t2 次数的。

f1t2<f2t1,此时第 2 段有 f2t1A 需要换到第 1 段,但第 1 段可以换过来的 B 只有 f1t2 个,不够,但第 2 段的 A 是必须换到第 1 段的,因此,只能先将第 1 段的 f2t1-f1t2C 先换到第 2 段,最后再将这 f2t1-f1t2C 交换到第 3 段。因此,需要的最少交换次数为

若 $f1t2>f2t1$,此时第 $2$ 段只有 $f2t1$ 个 $A$ 需要换到第 $1$ 段,但第 $1$ 段可以换过来的 $B$ 却有 $f1t2$ 个,多了。将这些多余的 $B$ 先换到第 $3$ 段中,到时会在第 $2$ 段的 $C$ 必须交换到第 $3$ 段时与它们交换,换回到第 $2$ 段,不会多增加交换次数。因此,需要的最少交换次数为 $f1t23+f2t3$。 所以,当 $f1t2<f2t1$ 时,最少交换次数为 $f1t23+f2t3+f2t1-f1t2$。这点也可以这样理解。在第 $1$ 段的 $f1t23$ 个非 $A$ 字母交换完成后,第 $2$ 段字母 $C$ 的个数就是交换前第 $2$ 段 $C$ 的个数($f2t3$)加上从第 $1$ 段换过来的 $C$ 的个数($f2t1-f1t2$)。 当 $f1t2\ge f2t1$ 时,最少交换次数为 $f1t23+f2t3$。 ## 2. 源程序。 ```c #include <stdio.h> #include <string.h> int calc(char s[],char a,char b,char c) // 将字符串s 按 ABC 顺序排列需要的最少交换次数 { int acnt=0,bcnt=0,ccnt=0; int f1t23=0,f1t2=0,f2t1=0,f2t3=0; int i; for (i=0;i<strlen(s);i++) // 统计字符串 s 中 A、B、C三种字符各自的个数 { if (s[i]==a) acnt++; else if (s[i]==b) bcnt++; else ccnt++; } for (i=0;i<acnt;i++) // 先看第1段,应该放acnt个字符A { if (s[i]!=a) f1t23++; // 第1段不是A的字符要换到后面2、3两段中 if (s[i]==b) f1t2++; // 第1段是B的字符,优先换到第2段中 } for (i=acnt;i<acnt+bcnt;i++) // 再第2段的情况,应该放bcnt个字符B { if (s[i]==a) f2t1++; // 第2段中的A 肯定换到第1段中 if (s[i]==c) f2t3++; // 肯定要换到第3段去 } int res = f1t23 + f2t3 ; if (f2t1>f1t2) res+=(f2t1 -f1t2); return res; } int main() { char s[100005]; scanf("%s",s); char c[][4]={"BAT","BTA","ABT","ATB","TAB","TBA"}; int ans=0x7f7f7f7f; int i; for (i=0;i<6;i++) { int t=calc(s,c[i][0],c[i][1],c[i][2]); if (t<ans) ans=t; } printf("%d\n",ans); return 0; } ```