Description
一次考试共有n个人参加,第i个人说:“有ai个人分数比我高,bi个人分数比我低。”问最少有几个人没有说真话(可能有相同的分数)
Input
第一行一个整数n,接下来n行每行两个整数,第i+1行的两个整数分别代表ai、bi
Output
一个整数,表示最少有几个人说谎
Sample Input
3
2 0
0 2
2 2
Sample Output
1
HINT
100%的数据满足: 1≤n≤100000 0≤ai、bi≤n
Solution
每个人的话相当于排名为[ai+1,n-bi]的人分数一样。
如果两个人的话所代表的区间有相交但不完全重合,那么就有人有两个不同的分数,所以这两个人的话冲突。
所以这道题就转变为了选择尽量多的不相交线段,简单的背包DP即可。
按右端点排序,然后右端点相同的用个vector存起来,区间相同合并、个数相加,然后直接区间端点转移。
别忘了细节:每个区间的左端点不大于右端点,且一条线段的长度不能小于线段数量。
代码
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <vector>
#define maxn 100010
using namespace std;
int n;
struct Data{
int a, b;
Data() {}
Data(int _a, int _b):a(_a), b(_b) {}
bool operator == (const Data& Q) const{
return a == Q.a && b == Q.b;
}
bool operator < (const Data& Q) const{
if(b == Q.b) return a < Q.a;
return b < Q.b;
}
}rin[maxn];
vector <Data> nico[maxn];
int dp[maxn];
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%d%d", &rin[i].a, &rin[i].b);
rin[i].a ++;
rin[i].b = n - rin[i].b;
}
sort(rin+1, rin+n+1);
for(int i = 1, j = 1; i <= n; i = j){
if(rin[i].a > rin[i].b){
j ++;
continue;
}
nico[rin[i].b].push_back(Data(rin[i].a, 0));
int siz = nico[rin[i].b].size();
while(rin[j] == rin[i]){
nico[rin[i].b][siz-1].b ++;
j ++;
}
}
for(int i = 1; i <= n; i++){
dp[i] = dp[i-1];
int siz = nico[i].size();
for(int j = 0; j < siz; j++)
dp[i] = max(dp[i], dp[nico[i][j].a-1] + min(nico[i][j].b, i-nico[i][j].a+1));
}
printf("%d\n", n - dp[n]);
return 0;
}