题意描述
你有一块长方形的巧克力,这块巧克力共有n*m小块。你想吃k小块巧克力,因此你也许需要掰开这块巧克力。
在每一次操作中你可以把任意一块矩形形状的巧克力掰成两块矩形形状的巧克力。你只能沿着巧克力小块之间的分割线掰开巧克力——可以沿着水平方向或是竖直方向掰开。掰开巧克力的花费等于分割线长度的平方。
例如,如果你有一块2*3的巧克力,那么你可以沿着水平方向掰从而得到两块1*3的巧克力,这次操作的花费即为3^2=9。或者你也可以沿着竖直方向掰从而得到一块21的巧克力和一块22的巧克力,这次操作的花费即为2^2=4。
对于每一个给出的n,m和k,计算出最小花费。你可以用多块巧克力凑出k小块巧克力。剩余的巧克力可以不是完整的一块。
输入格式:
输入数据的第一行包括1个整数t(1<=t<=40910),表示数据组数。
接下来的t行每一行都包含3个整数n,m和k(1<=n,m<=30,1<=k<=min(n*m,50)),其意义见上文。
输出格式:
输出t行,分别表示每一组数据的最小花费。
说明:
在样例一的第1行这组数据情况下一共需要进行2次操作: 1.把22的巧克力掰成两块21的巧克力,花费为2^2=4; 2.把21的巧克力掰成两块11的巧克力,花费为1^2=1。
在样例一的第2行这组数据情况下操作步骤同上。
此题n,m范围处于30,显然不可以状压,只能考虑高次方复杂度DP
我们发现一个大的巧克力可以切成 两块宽与它相同,长的和等于它的巧克力 或 两块长与它相同,宽的和等于它的巧克力
状态设计为f[i][j][k]表示i*j的巧克力得到大小为k的巧克力的最小花费
由此我们可以得到转移复杂度O(n*m*k*(n+m)*k)
一开始我居然把右移写成左移了,结果RE了
实现如下:
#include <algorithm> #include <iostream> #include <cmath> #include <cstring> #include <map> #include <string> #include <vector> #include <queue> #include <stack> #include <cstdio> #include <cstdlib> using namespace std; typedef long long ll; inline int read() { register int p(1),a(0);register char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-') ch=getchar(); if(ch=='-') p=-1,ch=getchar(); while(ch>='0'&&ch<='9') a=a*10+ch-48,ch=getchar(); return a*p; } const int N=40,M=1000; int f[N][N][M],t,xx,yy,kk; int main() { // freopen("input","r",stdin); // freopen("output","w",stdout); memset(f,0x3f,sizeof(f)); for(int i=0;i<=30;i++) for(int j=0;j<=30;j++) f[i][j][0]=0,f[i][j][i*j]=0; for(int i=1;i<=30;i++) for(int j=1,big=min(i*j,50);j<=30;j++,big=min(i*j,50)) for(int k=1;k<=big;k++) { for(int l=1,lar=min(j*l,k);l<=(i>>1);l++,lar=min(j*l,k)) for(int tt=0;tt<=lar;tt++) f[i][j][k]=min(f[i][j][k],f[l][j][tt]+f[i-l][j][k-tt]+j*j); for(int l=1,lar=min(i*l,k);l<=(j>>1);l++,lar=min(i*l,k)) for(int tt=0;tt<=lar;tt++) f[i][j][k]=min(f[i][j][k],f[i][l][tt]+f[i][j-l][k-tt]+i*i); } t=read(); while(t--) { xx=read(),yy=read(),kk=read(); printf("%d\n",f[xx][yy][kk]); } return 0; }