[BZOJ1786][BZOJ1831]逆序对

[BZOJ1786][BZOJ1831]逆序对

试题描述

输入

输出

输入示例

5 4
4 2 -1 -1 3

输出示例

4

数据规模及约定

见“输入

题解

首先这题有一个性质,即,填的数从左到右一定不降。证明不妨读者自己yy(提示:用先按降序填,交换后答案一定不会更差的思想证)。

那么新添的数字一定不会造出逆序对了。

然后设计 dp,设 f(i, j) 表示考虑前 i 个打“-1”的位置,最后一个(第 i 个)位置填写数字 j 时,与已经固定的数字产生最少的逆序对数。

有 f(i, j) = min{ f(i-1, x) | 0 < x ≤ j } + lager(pos[i], j) + smaller(pos[i], j),其中,pos[i] 表示第 i 个“-1”在原数列中的位置,larger(i, j) 表示第 1~i-1 个已知数中比 j 大的数的个数,smaller(i, j) 表示第 i+1~n 个已知数中比 j 小的数的个数。

答案是 min{ f(cnt, x) | 0 < x ≤ k } + ans,其中 cnt 表示 -1 的个数,ans 表示已知数字中逆序对个数。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <stack>
#include <vector>
#include <queue>
#include <cstdlib>
using namespace std;

const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *tail;
inline char Getchar() {
    if(Head == tail) {
        int l = fread(buffer, 1, BufferSize, stdin);
        tail = (Head = buffer) + l;
    }
    return *Head++;
}
int read() {
	int x = 0, f = 1; char c = Getchar();
	while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }
	while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }
	return x * f;
}

#define maxn 10010
#define maxk 110
#define oo 2147483647
int n, k, A[maxn], f[maxn][maxk], la[maxn][maxk], sm[maxn][maxk], pos[maxn], cnt;

int c[maxn];
void add(int x) { for(; x <= k; x += x & -x) c[x]++; return ; }
int sum(int x) { int res = 0; for(; x; x -= x & -x) res += c[x]; return res; }

int main() {
	n = read(); k = read();
	for(int i = 1; i <= n; i++) A[i] = read();
	
	int ans = 0;
	for(int i = n; i; i--) {
		if(A[i] >= 0) add(A[i]), ans += sum(A[i] - 1);
		for(int j = 2; j <= k; j++) sm[i][j] = sum(j - 1);
	}
	memset(c, 0, sizeof(c));
	for(int i = 1; i <= n; i++) {
		if(A[i] >= 0) add(k - A[i] + 1);
		else pos[++cnt] = i;
		for(int j = 1; j < k; j++) la[i][j] = sum(k - j);
	}
	for(int i = 1; i <= k; i++) f[0][i] = 0;
	for(int i = 1; i <= cnt; i++)
		for(int j = 1; j <= k; j++) {
			f[i][j] = oo;
			for(int x = 1; x <= j; x++) f[i][j] = min(f[i][j], f[i-1][x]);
			f[i][j] += la[pos[i]][j] + sm[pos[i]][j];
		}
	
	int sum = oo;
	for(int i = 1; i <= k; i++) sum = min(sum, f[cnt][i]);
	printf("%d\n", ans + sum);
	
	return 0;
}

 

转载于:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/5490776.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值