Solution
这题难在题意的转化。(同时说一句:STL大法好啊!!)
题目里给定了对于第 个人,有 个人分数比他高, 个人分数比他低,那么我们可以知道他的排名为 ,并且有 个人(包括自己)与他分数相等。那么我们就可以知道这段分数相等的区间为 。
显然如果 ,那么这个人说的一定是假话。同时如果有多个人分数相同,即在同一区间中,那么这些人的数量显然不能超过区间长度,如果超过,那么多余的这些人一定在说假话。
接着考虑怎么实现。首先肯定要记录同在一个区间内的人的数量,于是我们搬出STL——map。我们把区间插入到map中,并且记录当前区间的人数,对其进行累加操作。
接着把题目中“最小说假话人数”转换为“最大说真话人数”。我们定义 表示区间 中的人数,那么我们要求的就是从总区间 中选取几段不重叠的区间,使得区间的 之和最大。于是考虑 。
设 表示从 中选取不重叠的区间的 之和的最大值,那么首先显然要继承 的值。
接着考虑选取以
为右端点的区间,设此区间左端点为
,那么显然:
后面取
正是为了保证区间内人数不超过区间长度。
然后为了方便的找到以
为右端点的区间,我们在读入的时候进行预处理,开一个vector数组
(STL nb!!),
存放所有以
为右端点的区间的左端点,这样在
的时候就可以快速计算。
复杂度:由于 ,所以有一只 ,总复杂度 。
Code
别忘了最后答案是 。
#include <bits/stdc++.h>
#define MAX 100005
using namespace std;
vector<int> l[MAX];
map<pair<int, int>, int> mp;
int n, f[MAX];
int main()
{
cin >> n;
int x, y;
for(int i = 1; i <= n; i++){
scanf("%d%d", &x, &y);
x++, y = n-y;
if(x > y) continue;
int t = ++mp[make_pair(x, y)];
if(t == 1){
l[y].push_back(x);
}
}
for(int i = 1; i <= n; i++){
f[i] = f[i-1];
for(int j = 0; j < l[i].size(); j++){
x = l[i][j];
f[i] = max(f[i], f[x-1]+min(i-x+1, mp[make_pair(x, i)]));
}
}
cout << n-f[n] << endl;
return 0;
}