彩色方块 2017信息学夏令营第二场

NKOJ 4223 彩色方块

问题描述

何老板最近在玩一款叫“彩色方块”的小游戏,游戏虽然简单,但何老板仍旧乐此不疲。
游戏中有n个彩色方块成一排,方块的颜色用字母表示,给出目标排列,只要把它们排成跟目标一样的排列,就算过关。每次操作只能交换相邻两个方块。
给出一关游戏,何老板想知道,最少操作几次就能过关,请你帮他计算最少所需的操作步数。

输入格式

第一行,一个整数n表示方块的数量 第二行,n个大写字母,表示一开始方块的排列情况。每个字母表示对应方块的颜色。
第三行,n个大写字母,表示方块的目标排列情况。

输出格式

一行,一个整数,表示计算结果。 数据保证有解。

样例输入 1

3
RGB
GBR

样例输出 1

2

样例输入 2

4
AACB
BACA

样例输出 2

4

提示

样例1解释: RGB -> GRB -> GBR

对于100%的数据:2<=n<=1,000,000


就是求逆序对个数。

注意到“每次操作只能交换相邻两个方块”,想到了原来排序的题目:

给出一个数列。每次操作可以交换数列中相邻的两个数,求至少操作多少次,才能使数列变为非严格单调递增。

答案显然是原数列的逆序对个数。

本题只是改了一下,其本质还是这个模型。以样例输入2为例,如果将目标数列“BACA”标号为“1 2 3 4”,将原数列“AACB”标号为“2 4 3 1”,那么就完全变成上题的模型了。

简单地说,编号方法为:将目标数列编号为1~N,根据相应字母在目标数列中的编号来给原数列编号。如果同一字母有多个,则从小到大编号。
如:样例输入2中,A字母出现了两次,那么给原数列的两个A字母分别编为2,4。

求逆序对可以用归并排序实现,也可以通过树状数组实现。两个算法时间复杂度都是O(nlogn),权衡空间复杂度和编程难度,当然是优选树状数组。

注意N的范围较大,逆序对数量可能会超过int范围。

贴上辣鸡代码

#include<stdio.h>
#include<vector>
#define ll long long
using namespace std;
vector<int>h[30];
const int MAXN=1000005;

int N,cur[30],A[MAXN];ll Ans;
char ch[MAXN],t[MAXN];

ll C[MAXN];
void Modify(int x,ll d)
{
    for(int i=x;i<=N;i+=(i&-i))C[i]+=d;
}
ll GetSum(int x)
{
    ll sum=0,i;
    for(i=x;i;i-=(i&-i))sum+=C[i];
    return sum;
}

int main()
{
    int i,tmp,x;

    scanf("%d",&N);
    scanf("%s%s",ch,t);

    for(i=1;i<=N;i++)h[t[i-1]-'A'].push_back(i);//给目标数列编号 

    for(i=1;i<=N;i++)
    {
        x=ch[i-1]-'A';//找出原数列中字母对应编号,cur[x]用来标记当前讨论到'A'+x字母的序数 
        tmp=h[x][cur[x]];
        cur[x]++;
        Ans+=i-1-GetSum(tmp);
        Modify(tmp,1);
    }
    printf("%lld",Ans);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值