题目链接:http://codeforces.com/problemset/problem/347/B
题目意思:给出一个包含n个数的排列a,在排列a中最多只能作一次交换,使得ai = i 这样的匹配达到最多。
作一次交换,最理想的情况是,在原来匹配好的序列中再匹配到两个数;最坏的情况是,即使作怎样的交换,都不可能再找到可以匹配的两个数,也就是说,根本不需要作交换。至于一般情况下,是可以再匹配到一个数的。
我是设了两个数组(分别有n个数):a(用来存储待判断的序列a)和b(依次存储0~n-1个数)。然后判断a[i] 与b[i]是否相等,这是为了确定未作交换前两组序列本来能够匹配的数目;如果不符合就尝试交叉比较: a[i] = b[a[i]] 和 b[i] = a[b[a[i]]] 是否满足。若同时满足,代表做完一次交换可以达到最大的匹配,也就是匹配到2个。如果只满足其中一条,那么交换后可以达到一般的情况:匹配到一个数。否则,将会是最坏的情况,这时,干脆不要做交换。
举个例子:假设 a 序列为:0 1 2 6 7 5 3 1
b 序列为:0 1 2 3 4 5 6 7
第一次遇到的不匹配的数是 a[3], 由于考虑到b[i]存储数据的特殊性(依次存储0~n-1个数),因此通过 a[i] = b[a[i]] 的判断,a[3] = b[a[3]](b[6] = 6)和 b[3] = a[b[a[3]]] (a[b[6]] = a[6] = 3) , 发现两条式子都符合,那么可以达到最大的匹配。
还有一个非常值得注意的细节,序列a存储的数有可能比n-1要大(假设n = 8,a[i]有可能是9(0 <= i <= n-1),所以要先判断。
1 #include <iostream> 2 #include <stdio.h> 3 #include <stdlib.h> 4 using namespace std; 5 6 const int maxn = 100000 + 5; 7 int a[maxn], b[maxn]; 8 9 int main() 10 { 11 int i, n, cnt, flag, flag1; // flag用来标记达到最大的匹配数(2个),flag1标记一般情况交换后的匹配(1个) 12 while (scanf("%d", &n) != EOF) 13 { 14 for (i = 0; i < n; i++) 15 { 16 scanf("%d", &a[i]); 17 b[i] = i; 18 } 19 flag = flag1 = 0; 20 for (cnt = i = 0; i < n; i++) 21 { 22 if (a[i] == b[i]) // 统计未交换前的匹配数 23 cnt++; 24 else if (a[i] <= b[a[i]]) // a[i]有可能比n-1大 25 { 26 if (b[i] == a[b[a[i]]] && a[i] == b[a[i]]) 27 flag = 1; // 序列中至少存在一对通过交换后,在原来的基础上再匹配多2个数 28 else 29 flag1 = 1; // 交换一次,可以在原来匹配的基础上匹配多1个数 30 } 31 32 } 33 if (flag) 34 printf("%d\n", cnt+2); 35 else if (flag1) // 注意不能跟flag一起做判断条件,条件语句写成flag && flag1是错误的,这样写应归纳到到一个if的判断里 36 printf("%d\n", cnt+1); 37 else 38 printf("%d\n", cnt); // 怎样交换都不能再找到匹配,干脆一次都不交换 39 } 40 return 0; 41 }