NYOJ16 矩形嵌套 ——DP入门题

May 20, 2013
DP

题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=16 题目大意:   中文题…… 题目思路: 方法一:   先按照长和宽进行二级排序,然后转化成最长上升子序列求解。时间复杂度O(N^2),数据范围1000.


#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstring>
using namespace std;
const int MAX = 1000+10;
typedef struct node {
  int x, y;
  bool operator < (const node &other) const {
    if (x != other.x) {
      return x < other.x;
    } else return y < other.y;
  }
}node;
node ma[MAX];
int n, maxlen[MAX];
void init() {
  int i, a, b;
  scanf("%d", &n);
  for (i = 0; i < n; ++i) {
    scanf("%d%d", &a, &b);
    if (a > b) swap(a,b);
    ma[i].x = a; ma[i].y = b;
  }
  memset(maxlen, 0, sizeof(maxlen));
  maxlen[0] = 1;
}
bool judge(node a, node b) {
  if ((a.x<b.x && a.y<b.y) || (a.y<b.x && a.x<b.y)) return true;
  else return false;
}
void solve() {
  int i, j, tmp(0);
  sort(ma, ma+n);
  for (i = 1; i < n; ++i) {
    tmp = 0;
    for (j = 0; j < i; ++j) {
      if (judge(ma[j], ma[i])) {
        tmp = max(tmp, maxlen[j]);
      }
    }
    maxlen[i] = tmp + 1;
  }
  tmp = 0;
  for (i = 0; i < n; ++i) {
    tmp = max(tmp, maxlen[i]);
  }
  printf("%d\n", tmp);
}
int main(void) {
  int t; scanf("%d", &t);
  while (t--) {
    init();
    solve();
  }
  return 0;
}

其实这是很自然的思路 方法二:   如果一个矩形可以嵌套进另一个矩形中,那么就建一条矩形边。那么转化成有向无环图的最长路。d(i)表示从节点i出发的最长路的长度。 有:d(i) = max(d(i), d(j)+1| G[i][j]==1 )   然后就是所谓的记忆化搜索……   程序35行里面用了一个技巧,声明了一个引用,可以直接改变原来的变量的值,比较方便。  


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX = 1000+10;
int n;
typedef struct node{
  int x, y;
}node;
node ma[MAX];
int G[MAX][MAX], d[MAX];
bool judge(node a, node b) {
  if ((a.x<b.x && a.y<b.y) || (a.y<b.x && a.x<b.y)) return true;
  else return false;
}
void init() {
  int i, j, a, b;
  scanf("%d", &n);
  memset(G, 0, sizeof(G));
  memset(d, 0, sizeof(d));
  for  (i = 0; i < n; ++i) {
    scanf("%d%d", &a, &b);
    if (a > b) swap(a, b);
    ma[i].x = a; ma[i].y = b;
  }
  for (i = 0; i < n; ++i) {
    for (j = 0; j <n; ++j) {
      if (judge(ma[i], ma[j])) G[i][j] = 1;
    }
  }
}
int dp(int i) {
  int &ans = d[i];
  int j;
  if (ans > 0) return ans;
  ans = 1;
  for (j = 0; j < n; ++j) {
    if (G[i][j]) ans = max(ans, dp(j) + 1);
  }
  return ans;
}
void solve() {
  int i, ans = 0;
  for (i = 0; i < n; ++i) {
    d[i] = dp(i);
  }
  for (i = 0; i < n; ++i) {
    ans = max(ans, d[i]);
  }
  printf("%d\n", ans);
}
int main(void) {
  int t; 
  scanf("%d", &t);
  while (t--) {
    init();
    solve();
  }
  return 0;
}

  效率比方法一低 这个题目如果需要按照字典序打印出最终选择的矩形的编号,并且要求字典序最小,那么应该加上一个print_ans()函数,如下:  


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX = 1000+10;
int n;
typedef struct node{
  int x, y;
}node;
node ma[MAX];
int G[MAX][MAX], d[MAX];
bool judge(node a, node b) {
  if ((a.x<b.x && a.y<b.y) || (a.y<b.x && a.x<b.y)) return true;
  else return false;
}
void init() {
  int i, j, a, b;
  scanf("%d", &n);
  memset(G, 0, sizeof(G));
  memset(d, 0, sizeof(d));
  for  (i = 0; i < n; ++i) {
    scanf("%d%d", &a, &b);
    if (a > b) swap(a, b);
    ma[i].x = a; ma[i].y = b;
  }
  for (i = 0; i < n; ++i) {
    for (j = 0; j <n; ++j) {
      if (judge(ma[i], ma[j])) G[i][j] = 1;
    }
  }
}
int dp(int i) {
  int &ans = d[i];
  int j;
  if (ans > 0) return ans;
  ans = 1;
  for (j = 0; j < n; ++j) {
    if (G[i][j]) ans = max(ans, dp(j) + 1);
  }
  return ans;
}
void print_ans(int i)  {
  printf("%d ", i);
  int j;
  for (j = 0; j < n; ++j) {
    if (G[i][j] == 1 && d[i] == d[j] + 1) {
      print_ans(j); break;
    }
  }
}
void solve() {
  int i, ans = 0, index = 0;
  for (i = 0; i < n; ++i) {
    d[i] = dp(i);
  }
  for (i = 0; i < n; ++i) {
    if (ans < d[i]) {
      ans = d[i]; index = i;
    }
  }
  printf("%d\n", ans);
  print_ans(index);
  printf("\b\n");
}
int main(void) {
  int t; 
  freopen("16.in", "r", stdin);
  scanf("%d", &t);
  while (t--) {
    init();
    solve();
  }
  return 0;
}

  打印的思路就是从头开始,一次找下一个序号最小的点,同时满足最终的d[]数组之间关系的点。printf()里面有一个\b是为了不输出多余的空格,这个转义字符的作用是退格。  

comments powered by Disqus