[牛客OI周赛3-提高组B] 1408 [dp]

L i n k \frak{Link} Link

原题在 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{fi1,j+inv1i,j,fi,j1+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} 1i1 的白/黑球及 1 ∼ j \frak{1\sim j} 1j 的黑/白球)产生的逆序对数
因为要移回去啊。

#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值