BZOJ2298 [HAOI2011]problem a
Description
一次考试共有n个人参加,第i个人说:“有ai个人分数比我高,bi个人分数比我低。”问最少有几个人没有说真话(可能有相同的分数)
Input
第一行一个整数n,接下来n行每行两个整数,第i+1行的两个整数分别代表ai、bi
Output
一个整数,表示最少有几个人说谎
Sample Input
3
2 0
0 2
2 2
2 0
0 2
2 2
Sample Output
1
HINT
100%的数据满足: 1≤n≤100000 0≤ai、bi≤n
正解:DP
解题报告:
给出了多少个人更高、更低。那么对于每个人都可以得到一个rank的可行区间,可以把给定数据变成一些带权的线段。题目要求最小说谎数,可以先求最多可以有多少人说的是真话。我们需要求出选出一些线段,使得两两不覆盖的最大权值和。
我们考虑如何确定线段的权值,线段的权值肯定是代表这个线段的重复出现次数。选了这个线段就意味着同时可以满足那么多个人的话。但注意如果次数超过了区间长度显然是不可以的,因为区间最多只能有那么多个人,不可能更多人同分,取一个min就可以了。判重的话我开了个map记了一下。
DP的话固定右端点,然后对于所有以这个顶点为右端点的线段拿出来DP一下。
1 //It is made by jump~ 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 #include <algorithm> 8 #include <ctime> 9 #include <vector> 10 #include <queue> 11 #include <map> 12 #include <set> 13 #ifdef WIN32 14 #define OT "%I64d" 15 #else 16 #define OT "%lld" 17 #endif 18 using namespace std; 19 typedef long long LL; 20 const int MAXN = 100011; 21 int n,f[MAXN]; 22 map<pair<int,int>,int>mp; 23 vector<int>pre[MAXN]; 24 25 inline int getint() 26 { 27 int w=0,q=0; 28 char c=getchar(); 29 while((c<'0' || c>'9') && c!='-') c=getchar(); 30 if (c=='-') q=1, c=getchar(); 31 while (c>='0' && c<='9') w=w*10+c-'0', c=getchar(); 32 return q ? -w : w; 33 } 34 35 inline void work(){ 36 n=getint(); int x,y,len; 37 for(int i=1;i<=n;i++) { 38 x=getint(); y=getint(); 39 x++; y=n-y;//左右端点表示的是rank的可能区间 40 if(x>y) continue; 41 mp[make_pair(x,y)]++; 42 if(mp[make_pair(x,y)]==1) pre[y].push_back(x); 43 } 44 for(int i=1;i<=n;i++) { 45 f[i]=f[i-1]; len=pre[i].size(); 46 for(int j=0;j<len;j++) { 47 //相当于取带权线段不重叠,且权值和最大 48 //前一位的f加上这一个线段的权值,取min表示要么是这条线段出现次数,要么是线段长度(因为不可能超过区间长度个人说同样的话) 49 f[i]=max(f[pre[i][j]-1]+min(mp[make_pair(pre[i][j],i)],i-pre[i][j]+1),f[i]); 50 } 51 } 52 printf("%d",n-f[n]); 53 } 54 55 int main() 56 { 57 work(); 58 return 0; 59 }
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!