矩阵

14 篇文章 0 订阅

题目大意

求出一个n*m的矩阵中长至少为x宽至少为y的矩形价值的第K小。
每个元素都是非负整数,一个矩形的价值定义为所有元素和,相同价值的不同矩形在计算答案时算多个。

经典套路

观察一个以(x1,y1)为左上角的矩形。
假如右下角为(x2,y2),这个矩形一定比(x1,y1,x2+1,y2)和(x1,y1,x2,y2+1)都小。
所以先把所有位置为左上角的长为x宽为y的矩形扔进堆里。
每次取出一个矩形,然后将其往下和往右拓展一格的矩形扔进堆里。
我们这样可能拓展到同样的矩形,因此用map判重。
做k次即可。

#include<cstdio>
#include<algorithm>
#include<map>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
typedef pair<int,int> pi;
typedef pair<pi,pi> pii;
const int maxn=1000+10,maxtot=2000000+10;
struct dong{
    int x1,y1,x2,y2;
    ll data;
    friend bool operator <(dong a,dong b){
        return a.data<b.data;
    }
};
map<pii,bool> bz;
ll sum[maxn][maxn],a[maxn][maxn],cnt;
dong heap[maxtot],zlt;
int i,j,k,l,t,n,m,x,y,x1,y1,x2,y2,p,tot;
ll getsum(int x1,int y1,int x2,int y2){
    return sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1];
}
void put(dong x){
    heap[++tot]=x;
    int i=tot;
    while (i>1&&heap[i]<heap[i/2]){
        swap(heap[i],heap[i/2]);
        i/=2;
    }
}
void deletemin(){
    heap[1]=heap[tot];
    tot--;
    int i=1,j;
    while (i*2<=tot){
        if (i*2+1>tot||heap[i*2]<heap[i*2+1]) j=i*2;else j=i*2+1;
        if (heap[j]<heap[i]){
            swap(heap[i],heap[j]);
            i=j;
        }
        else break;
    }
}
bool pd(){
    int i,j,k,l;
    cnt=0;
    fo(i,1,n-x+1)
        fo(j,1,m-y+1)
            fo(k,x,n-i+1)
                fo(l,y,m-j+1){
                    cnt++;
                    if (cnt==p) return 1;
                }
    return 0;
}
bool dp(int x1,int y1,int x2,int y2){
    if (bz[make_pair(make_pair(x1,y1),make_pair(x2,y2))]) return 1;
    bz[make_pair(make_pair(x1,y1),make_pair(x2,y2))]=1;
}
int main(){
    scanf("%d%d%d%d%d",&n,&m,&x,&y,&p);
    fo(i,1,n)
        fo(j,1,m)
            scanf("%lld",&a[i][j]);
    fo(i,1,n)
        fo(j,1,m)
            sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
    if (!pd()){
        printf("-1\n");
        return 0;
    }
    fo(i,1,n-x+1)
        fo(j,1,m-y+1){
            zlt.x1=i;
            zlt.y1=j;
            zlt.x2=i+x-1;
            zlt.y2=j+y-1;
            bz[make_pair(make_pair(x1,y1),make_pair(x2,y2))]=1;
            zlt.data=getsum(i,j,i+x-1,j+y-1);
            put(zlt);
        }
    p--;
    while (p--){
        zlt=heap[1];
        deletemin();
        x1=zlt.x1;y1=zlt.y1;
        x2=zlt.x2;y2=zlt.y2;
        if (x2<n&&!dp(x1,y1,x2+1,y2)){
            zlt.x2++;
            zlt.data=getsum(x1,y1,x2+1,y2);
            put(zlt);
            zlt.x2--;
        }
        if (y2<m&&!dp(x1,y1,x2,y2+1)){
            zlt.y2++;
            zlt.data=getsum(x1,y1,x2,y2+1);
            put(zlt);
        }
    }
    zlt=heap[1];
    printf("%lld\n",zlt.data);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值