http://ace.delos.com/usacoprob2?a=kPNDMRfiMdD&S=sort3
这题初看似乎比较麻烦,仔细想想,也就这么回事了。
手工模拟一下排序的过程,然后对比排序前后的数列,很容易发现,交换只发生在以下情况:1.交换一次能够使得两个数都到达最终位置(这个显然的);2.两次交换能够使得三个数到达最终位置。只能有这两种情况了。
位置: 1 2 3 4 5 6 7 8 9
分析一下样例:2 2 1 3 3 3 2 3 1
最终位置是: 1 1 2 2 2 3 3 3 3
位置2、3交换一次,位置5、7交换一次,可以使两对数到达最终位置,此时:
位置: 1 2 3 4 5 6 7 8 9
2 1 2 3 2 3 3 3 1
最终位置是: 1 1 2 2 2 3 3 3 3
位置1,4,9交换两次,此时:
位置: 1 2 3 4 5 6 7 8 9
1 1 2 2 2 3 3 3 3
最终位置是: 1 1 2 2 2 3 3 3 3
任务完成,共交换4次。
至于两次交换使得3个数到达最终位置,具体的证明如下:
首先需要指明:a,b,c三个数之间可以有若干个其他数字,这里只取他们的相对位置。
假设a,b,c(a<b<c)三个数都不在他们的最终位置上(若有一个在最终位置上,归结到情况1),则只有以下两种情况:
最终位置:a b c
case1: b c a
case2: c a b
对于case1,交换b,c,然后交换a,c,两次可以完成;
对于case2,交换c,a,然后交换b,c,同样两次可以完成。
若a,b,c有两个数是相等的,则必定可以归结到情况1。这里不作证明。
到此,做法已经很明显了。下面给出步骤:
1.将数列排序,记录不在最终位置上的个数,用sn[i][j]表示在i的位置上有sn[i][j]个j(这里i和j都表示1,2,3其中一个);
2.统计在情况1的交换次数,取sn[i][j]和sn[j][i]的较小值;
3.统计情况2的交换次数,除去了情况1,若在1段和2段中发现有不合理的数字,则必须交换2次了,具体自己理解。
具体实现看代码:
#include <iostream> #include <string.h> #include <cstdio> using namespace std; int sn[4][4]={0}; int f[4]={0}; int a[100000]; int main() { freopen("sort3.in","r",stdin); freopen("sort3.out","w",stdout); int n; cin>>n; for (int i=1;i<=n;i++) { scanf("%d",&a[i]); f[a[i]]++; //排序 } //这一段代码求数组sn[i][j] int ll=1,rr=f[1]; for (int i=1;i<=3;i++) { for (int j=ll;j<=rr;j++) sn[i][a[j]]++; ll=rr+1; rr=rr+f[i+1]; } //情况1的统计,一次交换的条件是取sn[i][j]、sn[j][i]的较小值 int min1=min(sn[1][2],sn[2][1]),min2=min(sn[1][3],sn[3][1]), min3=min(sn[2][3],sn[3][2]); int sum=min1+min2+min3; //除去情况1,剩下的一定是情况2了 sum+=(sn[1][2]-min1+sn[1][3]-min2)*2; cout<<sum<<endl; return 0; }
这题其实挺水的。。。。附上我的战绩: