qzezoj 1580 巧克力

题面传送门
对于这道题,明显可以 d p dp dp,将一张表预处理出来。
我们可以设 f i , j , x , y , k f_{i,j,x,y,k} fi,j,x,y,k表示左下角为 i , j i,j i,j右上角为 x , y x,y x,y,切大小为 k k k的,转移时横竖各枚举切分点与 k k k的分配转移,时间复杂度 O ( 3 0 5 × 5 0 2 ) = O ( 1 ) O(30^5\times 50^2)=O(1) O(305×502)=O(1)
不过我们发现,实际上这道题任意大小一样的区间是没有区别的,也就是说任意 f i , j , x , y , k f_{i,j,x,y,k} fi,j,x,y,k f i + s , j + s , x + s , y + s , k f_{i+s,j+s,x+s,y+s,k} fi+s,j+s,x+s,y+s,k是没有区别的,于是我们就可以将所有这种区间转化成 f x − i , y − j , k f_{x-i,y-j,k} fxiyjk
那么思考一下这种区间怎么转移,仍旧枚举横竖区间,以竖区间为例,然后本来是转移 f i − x , j , k − y + f i − x + 1 i , j , y f_{i-x,j,k-y}+f_{i-x+1i,j,y} fix,j,ky+fix+1i,j,y的,但是这个区间可以映射到我们可以表示到的同一区间,就可以写出转移方程: f i , j , k = m i n { f i − x , j , k − y + f x , j , y + j 2 } f_{i,j,k}=min\{f_{i-x,j,k-y}+f_{x,j,y}+j^2\} fi,j,k=min{fix,j,ky+fx,j,y+j2},横区间同样。
时间复杂度 O ( 3 0 3 × 5 0 2 ) = 6750000 O(30^3\times 50^2)=6750000 O(303×502)=6750000,实际上还跑不满,再加上时限 2 s 2s 2s,可以跑过本题。
代码实现:

#include<cstdio>
#include<cstring>
inline int min(int a,int b){return a<b?a:b;}
using namespace std;
int n,m,k,f[39][39][139],z,a,b,c;
int main() {
//  freopen("1.in","r",stdin);
//  freopen("1.out","w",stdout);
    register int i,j,k,x,y;
    memset(f,0x3f,sizeof(f));
    for(i=1; i<=30; i++) {
        for(j=1; j<=30; j++) {
            f[i][j][i*j]=f[i][j][0]=0;
            for(k=1; k<=min(50,i*j); k++) {
                if(k!=i*j) {
                    f[i][j][k]=1e9;
                    for(x=1; x<i; x++) {
                        for(y=0; y<k; y++){
                            f[i][j][k]=min(f[x][j][y]+f[i-x][j][k-y]+j*j,f[i][j][k]);
                            //if(i==30&&j==29&&k==25) printf("%d %d %d %d %d\n",x,y,i-x,k-y,f[x][j][y]+f[i-x][j][k-y]+j*j);
                        }
                    }
                    for(x=1; x<j; x++) {
                        for(y=0; y<k; y++){
                            f[i][j][k]=min(f[i][x][y]+f[i][j-x][k-y]+i*i,f[i][j][k]);
                        //  if(i==30&&j==29&&k==25) printf("%d %d %d %d %d\n",x,y,j-x,k-y,f[i][x][y]+f[i][j-x][k-y]+i*i);
                        }
                    }
                }
            }
        }
    }
    //printf("%d\n",f[30][29][25]);
    scanf("%d",&n);
    while(n--) {
        scanf("%d%d%d",&a,&b,&c);
        printf("%d\n",f[a][b][c]);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值