POJ 2836 Rectangular Covering -状压DP+枚举

本文介绍了一种通过枚举矩形来寻找覆盖平面上多个点的最省地板砖总面积的方法。利用状态压缩技术,该算法能在合理的计算时间内找到最优解。

铺地板:坐标平面上有n各点,用任意大小(非零)的地板砖覆盖它们,求最省的地板砖总面积。


地板即矩形,平行于坐标轴,且必包含两个点,n<=15

可以枚举任意2个点作为对顶角点的矩形面积,并算出含多点,(状压到一个int)

可知道这些矩形必包含所有最优矩形。


遍历所有矩形,用每一个去更新所有状态

state=0:1<<n

根据dp[新矩形集合] = min(dp[新矩形集合], dp[旧矩形集合] + 新矩形的面积);

dp[s]表示矩形集合为s时,覆盖点情况为s的二进制情况,的最小面积

复杂度 

n*n*(2^n)

 



#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <iostream>
using namespace std;

const double pi=acos(-1.0);
double eps=0.000001;
typedef long long  ll;
const int N=20;
const int inf=1e9;

int dp[1<<16];
struct rec
{
    int cover;
    int area;
    rec() {}
    rec(int a,int b)
    {
        cover=a,area=b;
    }
    void add(int x)
    {
        cover|=1<<x;
    }
};
int xx[N],yy[N];
bool check(int i,int j,int k)  // 因为k夹在i,j之间,且矩形与x轴平行
{
    return
        (  (xx[i]-xx[k])*(xx[j]-xx[k])<=0) &&
        ( (yy[i]-yy[k])*(yy[j]-yy[k])<=0) ;

}

vector<rec > tt;
int main()
{

    int n;
    while(scanf("%d",&n)!=EOF)
    {
        if (!n)break;
        for (int i=0; i<n; i++)
            scanf("%d%d",&xx[i],&yy[i]);
        tt.clear();
        for (int i=0; i<n; i++)
            for (int j=i+1; j<n; j++)
            {
                rec tmp((1<<i)|(1<<j),max(1,abs(xx[i]-xx[j]))  *max(1,abs(yy[i]-yy[j]))   );

                for (int k=0; k<n; k++)
                {
                    if (check(i,j,k))
                        tmp.add(k);
                }
                tt.push_back(tmp);
            }

        int all=1<<n;
        for (int i=1; i<all; i++)  dp[i]=inf;
        dp[0]=0;
        for (int i=0; i<tt.size(); i++)
        {
            rec  tmp=tt[i];
            for (int j=0; j<all; j++)
            {
                int nex=j|tmp.cover;
                if (dp[j]!=inf && j!=nex)
                    dp[nex]=min(dp[nex],dp[j]+tmp.area);
            }
        }
        printf("%d\n",dp[all-1]);


    }
    return 0;

}


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值