题目大意:
给定的一个1,2,3组成的数字序列,计算排成升序所需的最少交换次数。
思路:
挺简单的题但是思路不好叙述。
题目中给出序列 2 2 1 3 3 3 2 3 1 ,其升序为 1 1 2 2 2 3 3 3 3 。可以根据对比发现升序中属于1的位置上在原序列中有两个2,可以记作a_b=2。其他依次可以记作a_c=0,b_a=1,b_c=2,c_a=1,c_b=1。
第一步:首先考虑a_b与b_a,要实现升序,此处肯定会交换min(a_b,b_a)次。交换一次可以使两个数到达正确位置。b_c,c_b同理,所以这种情况一共交换min(a_b,b_a)+min(b_c,c_b)次。此时序列为 1 2 2 2 3 3 3 3 1 。
第二步:这时可以发现只剩a_b=1,b_c=1,c_a=1,只需交换两次即可,即把属于1位置上的2和属于3位置上的1交换,再把属于2位置的3和属于3位置的2交换。交换完后可以发现序列已经变为升序。
代码:
/* ID: lujunda1 LANG: C++ PROG: sort3 */ #include<iostream> #include<stdio.h> using namespace std; int num[4]={0},arry[1000]={0}; //num[]用于记录序列中3个数字出现的次数。 //arry[]存储序列。 int min(int a,int b) { return a<b?a:b; } int main() { freopen("sort3.in","r",stdin); freopen("sort3.out","w",stdout); int n; scanf("%d",&n); for(int i=0;i<n;i++) { scanf("%d",&arry[i]); num[arry[i]]++; } int a_b=0,a_c=0,b_a=0,b_c=0,c_a=0,c_b=0; //a_b表示属于1的位置上出现2的次数。其他以此类推。 for(int i=0;i<n;i++) //通过循环对a_b、a_c等变量赋值。 if(i<num[1]) { if(arry[i]==2) a_b++; else if(arry[i]==3) a_c++; } else if(num[1]<=i&&i<num[1]+num[2]) { if(arry[i]==1) b_a++; else if(arry[i]==3) b_c++; } else if(num[1]+num[2]<=i&&i<n) { if(arry[i]==1) c_a++; else if(arry[i]==2) c_b++; } int sum=a_b+a_c+b_a+b_c+c_a+c_b,sub=min(a_b,b_a)+min(a_c,c_a)+min(b_c,c_b); //sub表示第一步所需的交换次数。 printf("%d\n",(sum-sub*2)/3*2+sub); //(sum-sub*2)/3*2表示第二步所需的交换次数。 }
为了对应思路,使表述清晰,才有了上述的代码。但是从实现角度来看,上面的代码未免有些不太文艺。下面是简化后的代码,代码缩减近10行。
/* ID: lujunda1 LANG: C++ PROG: sort3 */ #include<iostream> #include<stdio.h> using namespace std; int num[4]={0},arry[1000]={0},mark[4][4]={0}; //num[]用于记录序列中3个数字出现的次数。 //arry[]存储序列。 //mark[i][j]表示属于i的位置上出现j的次数。 int min(int a,int b) { return a<b?a:b; } int main() { freopen("sort3.in","r",stdin); freopen("sort3.out","w",stdout); int n; scanf("%d",&n); for(int i=0;i<n;i++) { scanf("%d",&arry[i]); num[arry[i]]++; } for(int i=0;i<n;i++) if(i<num[1]) mark[1][arry[i]]++; else if(num[1]<=i&&i<num[1]+num[2]) mark[2][arry[i]]++; else mark[3][arry[i]]++; int sum=0,sub=0; for(int i=1;i<=3;i++) for(int j=1;j<=3;j++) sum+=i-j?mark[i][j]:0; for(int i=2;i<=3;i++) for(int j=1;j<i;j++) sub+=min(mark[i][j],mark[j][i]); printf("%d\n",(sum-sub*2)/3*2+sub); }