搭积木
小明有一袋子长方形的积木,如果一个积木A的长和宽都不大于另外一个积木B的长和宽,则积木A可以搭在积木B的上面。好奇的小明特别想知道这一袋子积木最多可以搭多少层,你能帮他想想办法吗?
定义每一个长方形的长L和宽W都为正整数,并且1 <= W <= L <= INT_MAX, 袋子里面长方形的个数为N, 并且 1 <= N <= 1000000.
假如袋子里共有5个积木分别为 (2, 2), (2, 4), (3, 3), (2, 5), (4, 5), 则不难判断这些积木最多可以搭成4层, 因为(2, 2) < (2, 4) < (2, 5) < (4, 5)。
输入描述:
第一行为积木的总个数 N
之后一共有N行,分别对应于每一个积木的宽W和长L
输出描述:
输出总共可以搭的层数
示例1
输入
复制
5
2 2
2 4
3 3
2 5
4 5
原题:
题目链接
我的代码:
- 超时了
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
public class Main {
static class Block implements Comparable<Block>{
long x;
long y;
public Block(long x,long y) {
this.x = x;this.y = y;
}
@Override
public int compareTo(Block o) {
int dx = (int) (this.x - o.x);
if(dx==0) {
//判断dy
return (int) (this.y - o.y);
}else{
return dx;
}
}
}
public static void main(String[] args) throws IOException {
BufferedReader bf =
new BufferedReader(new InputStreamReader(System.in));
int n= Integer.parseInt(bf.readLine());
Block[] blocks = new Block[n];
int[]dp =new int[n+1];
for(int i=0;i<n;++i) {
String[]line = bf.readLine().split(" ");
blocks[i] = new Block(Integer.parseInt(line[0]),Integer.parseInt(line[1]));
dp[i] = 1;
}
bf.close();
Arrays.sort(blocks);
int res = 1;
for(int i=1;i<blocks.length;++i) {
for(int j = 0;j<i;++j) {
if(blocks[i].y>=blocks[j].y) {
dp[i] = Math.max(dp[i],dp[j]+1);
}
}
res = Math.max(dp[i],res);
}
System.out.println(res);
}
}
- 问了打 ACM同学的代码
#include <bits/stdc++.h>
#define MAX_INT ((unsigned)(-1)>>1)
#define MIN_INT (~MAX_INT)
#define db printf("where!\n");
#define pb push_back
using namespace std;
#define ll long long
ll gcd(ll x,ll y){return y ? gcd(y,x%y) : x;}
template<class T>inline void read(T &res){
char c;T flag=1;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
}
struct node
{
int a,b;
}d[1000005];
bool cmp(node a,node b)
{
if(a.a==b.a) return a.b<b.b;
return a.a<b.a;
}
int dp[1000005];
int main()
{
int n;read(n);
for(int i=1;i<=n;i++){
int a1,a2;
read(a1),read(a2);
d[i].a=a1,d[i].b=a2;
}
sort(d+1,d+1+n,cmp);
int len=0;
for(int i=1;i<=n;i++){
if(d[i].b>=dp[len]){
dp[++len]=d[i].b;
}
else{
int l=1,r=len;
while(l<r){
int mid=(l+r)/2;
if(dp[mid]>d[i].b) r=mid;
else l=mid+1;
}
dp[l]=d[i].b;
}
}
//for(int i=1;i<=n;i++) cout<<dp[i]<<" ";
cout<<len<<endl;
return 0;
}
- 最终的代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
public class Main {
static class Block implements Comparable<Block>{
int x;
int y;
public Block(int x,int y) {
this.x = x;this.y = y;
}
@Override
public int compareTo(Block o) {
int dx = (int) (this.x - o.x);
if(dx==0) {
//判断dy
return (int) (this.y - o.y);
}else{
return dx;
}
}
}
public static void main(String[] args) throws IOException {
BufferedReader bf =
new BufferedReader(new InputStreamReader(System.in));
int n= Integer.parseInt(bf.readLine());
Block[] blocks = new Block[n];
int[]dp =new int[n+1];
for(int i=0;i<n;++i) {
String[]line = bf.readLine().split(" ");
blocks[i] = new Block(Integer.parseInt(line[0]),Integer.parseInt(line[1]));
}
bf.close();
Arrays.sort(blocks);
int p = 0;
int[] tail = new int[n+1];
for(int i=0;i<n;++i) {
if(tail[p]<=blocks[i].y) {
tail[++p] = blocks[i].y;
}else{
int l = 0,r = p;
int find = blocks[i].y;
while(l<r) {
int mid = (l+r)>>1;
if(tail[mid]<find) {
// mid -> find
l = mid+1;
}else{
//tai[mid]> find find -> mid
r = mid;
}
}
tail[l] = find;
}
}
System.out.println(p);
}
}
思路: 这个题目的原题是 求最长上升子序列
也就是 leetCode的第300题
思路1 是不同解法,时间复杂度是 o(N^2)
可以通过 二分法 优化为 o(NlogN)
思路:贪心算法(二分法)
思路:每一次来一个新的数 num,在 tail 数组(tail 数组的定义在下面的示意图中有)中找大于等于 num 的那个数,试图让它变小,以致于新来的数有更多的可能性接在它后面,成为一个更长的“上升子序列”,这是“贪心算法”的思想。
在 tail 数组中找大于等于 num 的那个数,可以使用“二分法”