CodeForces Manthan 2011 D. Optical Experiment(动态规划)
题目大意
这题建议大家先看看原题是怎么描述的,在看看我讲的中文题面,本来我打算直接翻译的,但是考虑到文字太多,还是算了
我描述的中文题意:
题意化简之后就是一个矩形,上下两边各有 n(1≤n≤106) 个点,每个上边的点只会和下边的一个点相连,构成一条线段,那么就有了 n 条线段,现在问:这 n 条线段中,最多有多少线段,他们两两相交?输出这个最大的线段数量
做法分析
把矩形上边的点依次重新编号(从小到大),下边对应线段的点编上相同的号
假设 m 条线段两两相交,那么这 m 条线段在上边的编号必然是递增的,在下边的编号必然是递减的
那么问题转化成了在下边求一个最长下降的子序列,必须用 nlogn 的做法
PS:这题的关键是题意的转化以及对线段的重新编号,好题!出题人隐藏的太深!
参考代码
Optical Experiment
1 #include <cstring> 2 #include <cstdio> 3 #include <iostream> 4 #include <map> 5 6 using namespace std; 7 8 const int N=1000006; 9 10 map <int, int> ihash; 11 int n, s[N], t[N], f[N], len[N], ans; 12 13 int main() 14 { 15 scanf("%d", &n); 16 for(int i=0; i<n; i++) 17 { 18 scanf("%d", &s[i]); 19 ihash.insert(make_pair(s[i], i+1)); 20 } 21 int ans=0; 22 for(int i=0; i<n; i++) 23 { 24 scanf("%d", &t[i]); 25 t[i]=ihash[t[i]]; 26 } 27 memset(f, 0, sizeof f); 28 f[0]=1, len[1]=t[0], ans=1; 29 for(int i=1; i<n; i++) 30 { 31 int Min=len[f[i-1]]; 32 if(t[i]<Min) len[++ans]=t[i], f[i]=f[i-1]+1; 33 else 34 { 35 int L=1, R=ans; 36 while(L<R) 37 { 38 int mid=(L+R)>>1; 39 if(len[mid]<t[i]) R=mid; 40 else L=mid+1; 41 } 42 if(len[L]<t[i]) L--; 43 L++; 44 len[L]=t[i]; 45 f[i]=f[i-1]; 46 } 47 } 48 printf("%d\n", ans); 49 return 0; 50 }
题目链接 & AC通道
CodeForces Manthan 2011 D. Optical Experiment