【洛谷P4280】逆序对

题目

题目链接:https://www.luogu.com.cn/problem/P4280
暑假到了,小可可和伙伴们来到海边度假,距离海滩不远的地方有个小岛,叫做欢乐岛,整个岛是一个大游乐园,里面有很多很好玩的益智游戏。碰巧岛上正在举行“解谜题赢取免费门票”的活动,只要猜出来迷题,那么小可可和他的朋友就能在欢乐岛上免费游玩两天。
迷题是这样的:给出一串全部是正整数的数字,这些正整数都在一个范围内选取,谁能最快求出这串数字中“逆序对”的个数,那么大奖就是他的啦!
当然、主办方不可能就这么简单的让迷题被解开,数字串都是被处理过的,一部分数字被故意隐藏起来,这些数字均用-1来代替,想要获得大奖就必须求出被处理的数字串中最少能有多少个逆序对。小可可很想获得免费游玩游乐园的机会,你能帮助他吗?
注:“逆序对”就是如果有两个数A和B,A在B左边且A大于B,我们就称这两个数为一个“逆序对”,例如:4 2 1 3 3里面包含了5个逆序对:(4, 2)、(4, 1)、(4, 3)、(4, 3)、(2, 1)。
n ≤ 10000 , m ≤ 100 n\leq 10000,m\leq 100 n10000,m100

思路

不难发现填进去的数一定是单调不减的,手画分类讨论一下就可以得出。
然后就是一道裸的 dp 了。设 f i , j f_{i,j} fi,j 表示前 i i i 个位置,最后一个填的是 j j j 的最小逆序对数。注意这里只计算填入的数产生的逆序对数量,已经有的数可以直接计算出来最后加上。
预处理 g 0 / 1 , i , j g_{0/1,i,j} g0/1,i,j 表示前缀不小于 j j j 的数字数量,以及后缀不大于 j j j 的数字数量。然后转移就直接随便写写就行了。
时间复杂度 O ( n m ) O(nm) O(nm)

代码

#include <bits/stdc++.h>
using namespace std;

const int N=10010,M=110,Inf=1e9;
int n,m,cnt,a[N],f[N][M],g[2][N][M];

int main()
{
	scanf("%d%d",&n,&m);
	memset(f,0x3f3f3f3f,sizeof(f));
	f[0][1]=0;
	for (int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++)
			g[0][i][j]=g[0][i-1][j]+(a[i]>=j)*(a[i]!=-1);
	for (int i=n;i>=1;i--)
		for (int j=1;j<=m;j++)
			g[1][i][j]=g[1][i+1][j]+(a[i]<=j)*(a[i]!=-1);
	for (int i=1;i<=n;i++)
	{
		if (a[i]!=-1)
		{
			memcpy(f[i],f[i-1],sizeof(f[i]));
			cnt+=g[0][i-1][a[i]+1];
		}
		else
		{
			int minn=Inf;
			for (int j=1;j<=m;j++)
			{
				minn=min(minn,f[i-1][j]);
				f[i][j]=minn+g[0][i-1][j+1]+g[1][i+1][j-1];
			}
		}
	}
	int ans=Inf;
	for (int i=1;i<=m;i++)
		ans=min(ans,f[n][i]);
	printf("%d",ans+cnt);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值