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是为了不输出多余的空格,这个转义字符的作用是退格。