qzezoj 1580 巧克力
题面传送门
对于这道题,明显可以\(dp\),将一张表预处理出来。
我们可以设\(f_{i,j,x,y,k}\)表示左下角为\(i,j\)右上角为\(x,y\),切大小为\(k\)的,转移时横竖各枚举切分点与\(k\)的分配转移,时间复杂度\(O(30^5\times 50^2)=O(1)\)
不过我们发现,实际上这道题任意大小一样的区间是没有区别的,也就是说任意\(f_{i,j,x,y,k}\)与\(f_{i+s,j+s,x+s,y+s,k}\)是没有区别的,于是我们就可以将所有这种区间转化成\(f_{x-i,y-j,k}\)。
那么思考一下这种区间怎么转移,仍旧枚举横竖区间,以竖区间为例,然后本来是转移\(f_{i-x,j,k-y}+f_{i-x+1i,j,y}\)的,但是这个区间可以映射到我们可以表示到的同一区间,就可以写出转移方程:\(f_{i,j,k}=min\{f_{i-x,j,k-y}+f_{x,j,y}+j^2\}\),横区间同样。
时间复杂度\(O(30^3\times 50^2)=6750000\),实际上还跑不满,再加上时限\(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]);
}
}