NOIP2013火柴排队

3 篇文章 0 订阅
2 篇文章 0 订阅

http://codevs.cn/problem/3286/
思路
首先易知,当a,b两串都进行过排序后,各对应位置的差值平方之和最小。
简单证明:
设序列只有两个数分别是a,b;c,d;且a < b;c < d
设S1=(a-c)^2+(b-d)^2,S2=(a-d)^2+(b-c)^2,
S1-S2=2ad+2bc-2ac-2bd=2a(d-c)+2b(c-d)=2(c-d)(b-a)<0
S1 < S2。
若存在a=b或c=d的情况S1仍为最小距离。
那么问题就转化成了,已知a串 b串对应位置,每次交换b中相邻的两个数,求最小的移动步数使b串各位置与a串一一对应。
交换过程类似于冒泡排序,我们知道冒泡排序每次交换都会使原串逆序对数减少1,则交换次数 = b串相对于a串的逆序对数(以a串对应b串位置的大小关系为比较标准)。统计逆序对数即可。
代码

//归并排序求逆序对——O(nlogn)AC
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int mod = 99999997;
int n,ans1,ans2;
int A[1000010],B[1000010],As[1000010],Bs[1000010],mp[1000010],pos[1000010],tmp[1000010];
void merge_sort(int l,int r)
{
    if(l==r)
    return;
    int mid=(l+r)>>1;
    merge_sort(l,mid);
    merge_sort(mid+1,r);
    int i=l;
    int k=l;
    int j=mid+1;
    while(i<=mid&&j<=r)
    {
        if(pos[mp[B[i]]]>pos[mp[B[j]]])
        {
            tmp[k++]=B[j++];
            ans2=((ans2%mod)+((mid-i+1)%mod))%mod;//在从i开始到mid中的所有数都会与j位置的数形成一个逆序对 
        }
        else tmp[k++]=B[i++];
    }
    while(i<=mid)
    tmp[k++]=B[i++];
    while(j<=r)
    tmp[k++]=B[j++];
    for(int i=l;i<=r;i++)
    B[i]=tmp[i];
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&A[i]);
        As[i]=A[i];
        pos[A[i]]=i;//按编号比较 
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&B[i]);
        Bs[i]=B[i];
    }
    sort(As+1,As+n+1);
    sort(Bs+1,Bs+n+1);
    for(int i=1;i<=n;i++)
    {
        ans1+=(As[i]-Bs[i])*(As[i]-Bs[i]);
        mp[Bs[i]]=As[i];//最佳映射方案 
    }
    merge_sort(1,n);
    printf("%d",ans2);
    return 0;
}
//冒泡排序求逆序对——O(n^2)会导致TLE
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int mod = 99999997;
int n,ans1,ans2;
int A[1000010],B[1000010],As[1000010],Bs[1000010],mp[1000010],pos[1000010];
bool flag;
void bubble_sort(int l,int r)
{
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n-i;j++)
        {
            if(pos[mp[B[j]]]>pos[mp[B[j+1]]])
            {
                swap(B[j],B[j+1]);//每进行一次交换,逆序对的个数减少1 
                ans2++;
                flag=1;
            }
        }
        if(!flag)
        break;
        flag=0;
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&A[i]);
        As[i]=A[i];
        pos[A[i]]=i;//按编号比较 
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&B[i]);
        Bs[i]=B[i];
    }
    sort(As+1,As+n+1);
    sort(Bs+1,Bs+n+1);
    for(int i=1;i<=n;i++)
    {
        ans1+=(As[i]-Bs[i])*(As[i]-Bs[i]);
        mp[Bs[i]]=As[i];//最佳映射方案 
    }
    bubble_sort(1,n);
    printf("%d",ans2);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值