题目大意:
网址:https://www.luogu.org/problemnew/show/2519
大意:
一次考试共有\(n\)个人参加,
第\(i\)个人说:“有\(a_i\)个人分数比我高,\(b_i\)个人分数比我低。”问最少有几个人没有说真话(可能有相同的分数)
数据范围:$ 1≤n≤100000 0≤a_i、b_i≤n$
题目解法:
有\(a_i\)个人分数比我高,\(b_i\)个人分数比我低,
意味着这个人在成绩序列中处在\([a_i+1 , n - b_i]\)中。
并且\([a_i+1 , n - b_i]\)这个区间中的人成绩都是一样的。
显然可以\(DP\)了。
\(f[i]\)表示到了成绩序列的第\(i\)个位置,说真话的最大人数。
那么假设存在这样一个区间\([t,i]\),说自己属于此区间的有\(sum\)个人。
则转移:\[f[i] = max(f[i] , f[t-1] + sum);\]
用\(vector\)挂链,然后大力\(DP\)跑即可啦。(这题难度主要在模型的转化)
实现代码:
#include<bits/stdc++.h>
#define maxn 1000005
#define ll long long
#define gi(x) scanf("%lld",&x)
using namespace std;
vector<ll>gp[maxn]; ll nw,n,a,b,l,r,f[maxn],t[maxn];
int main(){
gi(n);
for(ll i = 1; i <= n; i ++){
gi(a); gi(b);
l = a+1; r = n-b;
if(l<=r)gp[r].push_back(l); //如果l>r那么肯定是假话。
}
for(ll i = 1; i <= n; i ++){
f[i] = f[i-1];
if(gp[i].empty())continue;
int m = gp[i].size();
for(ll j = 0; j < m; j ++)t[j+1] = gp[i][j];
sort(t+1,t+m+1);
for(ll j = 1,sum = 0; j <= m+1; j ++){
if(t[j]==t[j-1]){sum++;continue;}
if(sum)f[i] = max(f[i] , f[t[j-1]-1] + min(i-t[j-1]+1 , sum));
sum = 1;
}
}
cout<<n - f[n]; return 0;
}