排序是一种很频繁的计算任务。现在考虑最多只有三值的排序问题。一个实际的例子是,当我们给某项竞赛的优胜者按金银铜牌排序的时候。在这个任务中可能的值只有三种1,2和3。我们用交换的方法把他排成升序的。
写一个程序计算出,给定的一个1,2,3组成的数字序列,排成升序所需的最少交换次数。
PROGRAM NAME: sort3
INPUT FORMAT:
(file sort3.in)
第一行:
奖牌个数N (1 <= N <= 1000)
第 2行到第N+1行:
每行一个数字,表示奖牌。共N行。(1..3)
OUTPUT FORMAT:
(file sort3.out)
共一行,一个数字。表示排成升序所需的最少交换次数。
9 2 2 1 3 3 3 2 3 1
4
1.3第一个TEXT说过是贪心解法,幸亏还记得住。
官方题解:
Algorithm The sequence has three parts: the part which will be 1 when in sorted order, 2 when in sorted order, and 3 when in sorted order. The greedy algorithm swaps as many as possible of the 1's in the 2 part with 2's in the 1 part, as many as possible 1's in the 3 part with 3's in the 1 part, and 2's in the 3 part with 3's in the 2 part. Once none of these types remains, the remaining elements out of place need to be rotated one way or the other in sets of 3. You can optimally sort these by swapping all the 1's into place and then all the 2's into place.
算法:排序后的序列分为三个部分:排序后应存储1的部分,排序后应存储2的部分和排序后应存储3的部分,贪心排序法应交换尽量多的交换后位置正确的(2,1)、(3,1)和(3,2)数对。当这些数对交换完毕后,再交换进行两次交换后位置正确的(1,2,3)三个数。
Analysis: Obviously, a swap can put at most two elements in place, so all the swaps of the first type are optimal. Also, it is clear that they use different types of elements, so there is no ``interference'' between those types. This means the order does not matter. Once those swaps have been performed, the best you can do is two swaps for every three elements not in the correct location, which is what the second part will achieve (for example, all the 1's are put in place but no others; then all that remains are 2's in the 3's place and vice-versa, and which can be swapped).
分析:很明显,每一次交换都可以改变两个数的位置,若经过一次交换以后,两个数的位置都由错误变为了正确,那么它必定最优。同时我们还可发现,经过两次交换后,我们可以随意改变3个数的位置。那么如果存在三个数恰好为1,2和3,且位置都是错误的,那么进行两次交换使它们位置正确也必定最优。有由于该题具有最优子结构性质,我们的贪心算法成立。
/* ID: your_id_here PROG: sort3 LANG: C++ */ #include <cstdio> #include <algorithm> using namespace std; int main() { int i,a[1001],index[4],n,err[4][4],ans,tmp; freopen("sort3.in","r",stdin); freopen("sort3.out","w",stdout); while(1==scanf("%d",&n)) { index[1]=index[2]=index[3]=0; for(i=0;i<n;++i) { scanf("%d",a+i); ++index[a[i]]; } index[2]+=index[1];//index[i]表示排序后的序列中i的结尾的后一个下标 index[3]+=index[2]; err[1][2]=err[2][1]=err[1][3]=err[3][1]=err[2][3]=err[3][2]=0; for(i=0;i<index[1];++i) ++err[1][a[i]];//err[i][j]表示在应该放i的位置有几个j for(;i<index[2];++i) ++err[2][a[i]]; for(;i<index[3];++i) ++err[3][a[i]]; tmp=min(err[1][2],err[2][1]);//1和2最多能互换的个数 ans=tmp,err[1][2]-=tmp,err[2][1]-=tmp; tmp=min(err[1][3],err[3][1]); ans+=tmp,err[1][3]-=tmp,err[3][1]-=tmp; tmp=min(err[2][3],err[3][2]); ans+=tmp,err[2][3]-=tmp,err[3][2]-=tmp; printf("%d\n",ans+((err[1][2]+err[1][3])<<1));//最后剩下的一定是不能一次换到正确位置的 } return 0; }