洛谷P1034 【矩形覆盖】

Dfs+剪枝 保证正确性

复杂度:\(O(玄学)\)

此题标签是计算几何,可以极角排序然后DP,然而我因为想练搜索写了这份代码

对于每一个点,我们枚举它在哪一个矩形中。即对于每一个点,可以创建一个新矩阵储存它,也可以用旧的矩阵储存它。我们可以用vector来储存每个矩形中的点,当矩形中的点确定,矩形的大小和位置就是唯一确定的。

当n个点被放入k个矩形中时,我们计算出矩形的面积总和并更新答案。因为矩形不能重合,而矩形重合当且仅当一个矩形的一个顶点至少一个在另一个矩形中,两两枚举判断即可

光这样是无法AC此题的,我们还需要剪枝。显然随着点数的增加,矩形面积单调递增。所以我们每次计算出矩形的面积判断是否比答案优,即可AC此题

还有一个小剪枝,如果当前使用矩阵+未使用的点数<k,可以直接return

枚举K的做法只能通过K<=3的数据,不够严谨,而DP需要计算几何知识,对蒟蒻不友好,还是深搜最有保证

#include<cstdio>
#include<string>
#include<vector>
#include<cstring>
using namespace std;

#define go(i,a,b) for(int i=a;i<=b;++i)
#define com(i,a,b) for(int i=a;i>=b;--i)
#define mem(a,b) memset(a,b,sizeof(a))

const int N=55,inf=0x3f3f3f3f;

int n,K,x1[5],y1[5],x2[5],y2[5],x[N],y[N],last=inf;
vector<int>sq[5];

inline void read(int &x){
    x=0;char f=1,c=getchar();
    while(!isdigit(c)){ if(c=='-') f=-1; c=getchar(); }
    while(isdigit(c)){ x=x*10+c-'0'; c=getchar(); }
    x*=f;
}

inline bool pd(int i,int j){
    if(x1[i]>=x1[j]&&x1[i]<=x2[j]&&y1[i]>=y1[j]&&y1[i]<=y2[j]) return 0;
    if(x2[i]>=x1[j]&&x2[i]<=x2[j]&&y1[i]>=y1[j]&&y1[i]<=y2[j]) return 0;
    if(x2[i]>=x1[j]&&x2[i]<=x2[j]&&y2[i]>=y1[j]&&y2[i]<=y2[j]) return 0;
    if(x1[i]>=x1[j]&&x1[i]<=x2[j]&&y2[i]>=y1[j]&&y2[i]<=y2[j]) return 0;
    return 1;
}

inline int sum(int num){
    mem(x1,0x3f),mem(y1,0x3f);
    mem(x2,0),mem(y2,0);
    go(i,1,num-1)
        for(int j=0;j<sq[i].size();++j){
            x1[i]=min(x1[i],x[sq[i][j]]);
            y1[i]=min(y1[i],y[sq[i][j]]);
            x2[i]=max(x2[i],x[sq[i][j]]);
            y2[i]=max(y2[i],y[sq[i][j]]);
        }
    go(i,1,num-1)
        go(j,i+1,num-1){
            if(!pd(i,j)) return inf;
        }
    static int ans;
    ans=0;
    go(i,1,num-1) ans+=(x2[i]-x1[i])*(y2[i]-y1[i]);
    return ans;
}

void dfs(int p,int num){
    static int ans;
    if((ans=sum(num))>=last) return;
    if(n-p+num<K) return;
    if(p==n+1&&num==K+1){ if(ans<last) last=ans; } return; }
    if(num<=K){
        sq[num].push_back(p);
        dfs(p+1,num+1);
        sq[num].pop_back();
    }
    if(num>1){
        go(i,1,num-1){
            sq[i].push_back(p);
            dfs(p+1,num);
            sq[i].pop_back();
        }
    }
}

int main(){
    //freopen("input.txt","r",stdin);
    read(n),read(K);
    go(i,1,n) read(x[i]),read(y[i]);
    dfs(1,1);
    printf("%d",last);
    return 0;
}

转载于:https://www.cnblogs.com/White-star/p/11579501.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值