原题在 a t c o d e r \frak{atcoder} atcoder上
下面有一大段废话。
其实这道题挺简单的
有两种东西混一起的题目已经有不少了,不过我还是没想出来
主要是根本没有往
d
p
\frak{dp}
dp上面想,看见排序题就想着怎么贪心了(
有一类题形如通过交换使序列有序的
最基本的问题是交换相邻两个数字/任意两个数字使序列有序求最少交换次数
两道经典题目:花匠、花火
交换区间使序列有序这种没找到题目
交换相邻两数使区间有序
交换任意两数使区间有序(也没找到)
(第二个基本问题(交换任意两个数使序列有序),答案是数的个数减掉轮换数)
(这里的轮换数是指,把每个数跟它在有序序列里的位置连个线,成环的个数)
其中第一个基本问题的答案是逆序对个数。
实际上并不一定要是标准定义的“逆序对”——“逆序”的定义其实可以重载
比如可以推广为“排序前
a
\frak{a}
a在
b
\frak{b}
b前面→排序后
a
\frak{a}
a在
b
\frak{b}
b后面”的
a
\frak{a}
a和
b
\frak{b}
b
那么这道题目答案就是逆序对个数。现在不确定的就是目标状态。
这题本质上就是求移动到最优解的最少步数。
混色题目很多了,应该要很容易想到黑白球分两维的
d
p
\frak{dp}
dp。
记
f
i
,
j
\frak{f_{i,j}}
fi,j为使(前
i
\frak{i}
i个白球有序,前
j
\frak{j}
j个黑球有序)的最小代价。现在看起来转移方程挺显然的。
转移考虑当前这个位置放黑球
j
\frak{j}
j还是白球
i
\frak{i}
i,
f
i
,
j
=
m
i
n
{
f
i
−
1
,
j
+
i
n
v
1
i
,
j
,
f
i
,
j
−
1
+
i
n
v
2
j
,
i
}
\frak{f_{i,j}=min\{f_{i-1,j}+inv1_{i,j},f_{i,j-1}+inv2_{j,i}\}}
fi,j=min{fi−1,j+inv1i,j,fi,j−1+inv2j,i}
i
n
v
1
/
2
i
,
j
\frak{inv1/2_{i,j}}
inv1/2i,j 表示第
i
i
i 个白/黑球与(
1
∼
i
−
1
\frak{1\sim i-1}
1∼i−1 的白/黑球及
1
∼
j
\frak{1\sim j}
1∼j 的黑/白球)产生的逆序对数
因为要移回去啊。
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<ctime>
#include<cstring>
using namespace std;
int n,pos[2][4005]={},cost[2][2005][2005]={};
int f[2005][2005]={};
char ch;
int main()
{
scanf("%d",&n);
for(register int id,i=1;i<=n+n;++i)
scanf(" %c %d",&ch,&id),pos[ch=='W'][id]=i;
for(register int k=0;k<=1;++k)
for(register int i=1;i<=n;++i)
for(register int j=i+1;j<=n;++j)
cost[k][i][n]+=(pos[k][j]<pos[k][i]);
for(register int k=0;k<=1;++k)
for(register int i=1;i<=n;++i)
for(register int j=n-1;j>=0;--j)
cost[k][i][j]=cost[k][i][j+1]+(pos[k][i]>pos[!k][j+1]);
memset(f,0x3f,sizeof(f)); f[0][0]=0;
for(register int i=0;i<=n;++i)
for(register int j=0;j<=n;++j)
(i?(f[i][j]=min(f[i][j],f[i-1][j]+cost[1][i][j])):0),
(j?(f[i][j]=min(f[i][j],f[i][j-1]+cost[0][j][i])):0);
printf("%d",f[n][n]);
return 0;
}