有 (n) 个人参加考试,第 (i) 个人说,有 (a_i) 个人分数比他高,(b_i) 个人分数比他低。求至少有多少人说了假话。
Solution
设 (l_i=a_i+1, r_i=n-b_i),则假如第 (i) 个人说了真话,那么与第 (i) 个人分数相同的所有人的名次区间为 ([l_i,r_i])
- 如果 (l_i>r_i),那么这个人直接删去
- 如果 ((l_i,r_i)) 相同的人超过了 (r_i-l_i+1) 个,就将多出来的人删去
在余下的区间中,每个区间设定一个权值 (v) 表示它的重数,现在我们就是要选出若干个不相交的区间使得他们的权值和最大,暴力 dp 即可
考虑将所有区间先按照右端点排序,依次扫描,设 (f[i]) 为扫描到第 (i) 个区间的最优解,于是我们需要二分找到一个最大的 (k) 使得 (r_k<l_i),则 (f[i]=max(f[i-1],f[k]+v_i))
另一种方法是,设 (f[i]) 为扫描到 (i) 位置时的最优解,那么仍然按照 (r) 升序枚举所有区间来转移,转移方程为
[f[r_i]=max(f[r_i], f[l_i-1]+v_i) \
f[i]=max(f[i],f[i-1])
]
频繁打错变量名……
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
struct range {
int l,r,v;
bool operator < (const range &b) {
if(r == b.r) return l < b.l;
else return r < b.r;
}
} s[N],x[N];
int n,a[N],b[N],f[N],ind;
signed main() {
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++) {
cin>>a[i]>>b[i];
s[i].l=a[i]+1;
s[i].r=n-b[i];
}
sort(s+1,s+n+1);
for(int i=1;i<=n;i++) {
if(s[i].l>s[i].r) continue;
if(x[ind].l!=s[i].l || x[ind].r!=s[i].r) {
x[++ind]=s[i];
}
x[ind].v++;
}
for(int i=1;i<=ind;i++) {
x[i].v=min(x[i].v,x[i].r-x[i].l+1);
for(int j=x[i-1].r+1;j<=x[i].r;j++) f[j]=max(f[j],f[j-1]);
f[x[i].r]=max(f[x[i].r],f[x[i].l-1]+x[i].v);
}
int ans=0;
for(int i=1;i<=n;i++) ans=max(ans,f[i]);
cout<<n-ans;
}