POJ2836 状态压缩dp不能逆向推倒的

来源:互联网 发布:windows.old文件夹 编辑:程序博客网 时间:2024/05/17 08:17

状态压缩DP

题意:

Description

n points are given on the Cartesian plane. Now you have to use some rectangles whose sides are parallel to the axes to cover them. Every point must be covered. And a point can be covered by several rectangles. Each rectangle should cover at least two points including those that fall on its border. Rectangles should have integral dimensions. Degenerate cases (rectangles with zero area) are not allowed. How will you choose the rectangles so as to minimize the total area of them?

Input

The input consists of several test cases. Each test cases begins with a line containing a single integer n (2 ≤ n ≤ 15). Each of the next n lines contains two integers xy (−1,000 ≤ xy ≤ 1,000) giving the coordinates of a point. It is assumed that no two points are the same as each other. A single zero follows the last test case.

Output

Output the minimum total area of rectangles on a separate line for each test case.

Sample Input

20 11 00

Sample Output

1

Hint

The total area is calculated by adding up the areas of rectangles used.


分析的时候自己犯了一个低级的错误,这个题目,n比较小,明显的复杂度是 n*n * 2 ^ n

先预处理出选取两个点,这两个点的面积,和所覆盖的点。

逆向分析,先给定一个状态state,然后从这个状态向前转移。

这样其实是不行的,很有意思,这个地方,如果正着考虑,那么这个集合的状态state是由前面的一个状态 or 一个集合得到的,因此,从后面不能够直接进行推导。

因此我们采用正向的思维方法,使用每一个选择的点对,去更新每个待定的状态。

其实这样还能避免了逆向思维的一个缺点,

假如,你选择了一个点对进行状态转移,那么你是将现在的状态state中的那些【i】【j】包围的点,全部剔除掉还是保留一部分?

这个需要确定,再引进2^n的复杂度,就不行了。


贴上一份个人感觉写的非常漂亮的代码:

#include <cstdio>#include <cstring>#include <cmath>#include <algorithm>using namespace std;struct Rect {    int area;    int pset;}rect[300];int dp[1<<16];int x[20], y[20];int n;int main() {    //freopen("in.txt", "r", stdin);    while(scanf("%d", &n) && n) {        for(int i = 0; i < n; i++)            scanf("%d%d", &x[i], &y[i]);        int cnt = 0;        for(int i = 0; i < n-1; i++) {            for(int j = i+1; j < n; j++) {                rect[cnt].area = max(1, abs(x[i]-x[j]))*max(1, abs(y[i]-y[j]));                rect[cnt].pset = (1<<i) | (1<<j);                for(int k = 0; k < n; k++) {                    if((x[i]-x[k])*(x[j]-x[k]) <= 0 && (y[i]-y[k])*(y[j]-y[k]) <= 0)  //判断是不是在那个矩形的范围之内                        rect[cnt].pset |= 1<<k;                }                cnt++;            }        }        fill(dp, dp+(1<<16), 1<<30);        dp[0] = 0;        for(int i = 0; i < cnt; i++) {            for(int j = 0; j < 1<<n; j++) {                int p = j|rect[i].pset;                if(dp[j] != 1<<30 && p != j) {                    dp[p] = min(dp[p], dp[j]+rect[i].area);  //因为有可能有很多个状态都指向这个状态,如果反向的搜索,复杂度会升高,并且有很多没有意义的搜索                }            }        }        printf("%d\n", dp[(1<<n)-1]);    }    return 0;}



0 0
原创粉丝点击