USACO-Sorting a Three-Valued Sequence

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;
}

这题其实挺水的。。。。附上我的战绩:

sort3

posted @ 2012-10-22 17:14  ay27  阅读(170)  评论(0编辑  收藏  举报