题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1051
贪心策略,按照l为第一关键字,w为第二关键字排序,在排序后的w数组中寻找不下降子序列的个数即可(因为此时l数组单调不下降一定满足题目条件)。
有一个结论:单调不下降(不上升)子序列的数目等于最长单调下降(上升)子序列的长度,用树状数组求解LIS即可。
LIS问题用树状数组求解,树状数组直接维护前i-1个序列元素的LIS的最大值,树状数组的下标对应序列元素的高度(这样可以直接查询高度大于(等于)或小于(等于)个高度的LIS的最大值),序列下标较小的元素先进树状数组,保证了树状数组里查询到的结果一定是前i-1个元素而非之后的元素的。
看到这题的n为5000,感觉n^2肯定会炸,就写了一个LIS,结果发现其实交一个暴力的话只需要15ms,心情复杂.jpg
LIS树状数组做法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
using namespace std;
const int maxn=5009;
const int maxl=10009;
const int mw=10001;
struct W {
	int a,b;
	bool operator <(const W& y) const {
		if(a!=y.a) return a<y.a;
		else return b<y.b;
	}
}w[maxn];
int n;
int f[maxn];
int t[maxl];
int lowbit(int x) {
	return x&-x;
}
void add(int x,int val) {
	while(x<maxl) {
		t[x]=max(t[x],val);
		x+=lowbit(x);
	}
}
int query(int x) {
	int ans=-2147483648;
	while(x) {
		ans=max(ans,t[x]);
		x-=lowbit(x);
	}
	return ans;
}
int main() {
	int T;
	scanf("%d",&T);
	while(T--) {
		memset(f,0,sizeof(f));
		memset(t,0,sizeof(t));
		scanf("%d",&n);
		for(int i=1;i<=n;i++) {
			scanf("%d%d",&w[i].a,&w[i].b);
		}
		int mx=0;
		sort(w+1,w+n+1);
		for(int i=1;i<=n;i++) {
			f[i]=query(mw-(w[i].b+1))+1;
			mx=max(mx,f[i]);
			add(mw-(w[i].b),f[i]);
		}
		printf("%d\n",mx);
	}
}
暴力做法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
using namespace std;
const int maxn=5009;
struct W {
	int a,b;
	bool operator <(const W& y) const {
		if(a!=y.a) return a<y.a;
		else return b<y.b;
	}
}w[maxn];
int n;
bool vis[maxn];
int main() {
	int T;
	scanf("%d",&T);
	while(T--) {
		scanf("%d",&n);
		memset(vis,0,sizeof(vis));
		for(int i=1;i<=n;i++) {
			scanf("%d%d",&w[i].a,&w[i].b);
		}
		sort(w+1,w+n+1);
		int cnt=0,x;
		for(int i=1;i<=n;i++)
			if(!vis[i]) {
				vis[i]=1;
				cnt++;
				x=w[i].b;
				for(int j=i+1;j<=n;j++) 
					if(w[j].b>=x && !vis[j]) x=w[j].b,vis[j]=1;
			}
		printf("%d\n",cnt);
	}
}