您的位置:首页 > 其它

洛谷 P1436 棋盘分割

2018-02-26 15:31 246 查看
如图:





思路:
这是一个很明显的二维dp题,每一次分割的可以衍生出2种情况:左(上)右(下)2个新矩形
所以最优解一定存在于两种新情况中,继续进行递归求解即可;
dp[左上坐标x][左上坐标y][右下坐标x][右下坐标y][分割次数];
顺推:dp[i][j][k][l][num] -> 新的上(左)(下)(右)矩形dp+   切割零次时剩余部分 sum^2;
逆推:dp[i][j][k][l][num] <- 旧的上(左)(下)(右)矩形dp(需要讨论)+  切割零次时另剩余部分 sum^2;

方程:  
    dp[i][j][k][l][num]=min(横着枚举切割方法,竖着枚举切割方法);//原方程太长,在程序里;
但是这道题的方程并不是最最重要的问题,推出了方程一样T的不要不要的
我们在枚举切割方法的时候,需要求出切割出的矩形的 值的平方,但每次都暴力/自定义函数 算一遍的话太浪费了
于是很流畅的就使得我们想到预处理的办法,利用简单的容斥原理,就可以在dp[i][j][k][l][0]上记录一遍不分割
的时候的平方和。
本蒟蒻是通过求了两遍 前缀和得到的差不多为 O(n^2+n^4)(n=8)算的,因为n不大,所以不是很介意;
总复杂度约为 O(8^5*n次询问+枚举均摊+8^2+8^4) ->O(一个大常数)#include<bits/stdc++.h>
using namespace std;
int n,x;
int sum[9][9];
int d,b[9][9];
int dp[9][9][9][9][16];
int main(void){
scanf("%d",&n);
for(int i=1;i<=8;i++){
for(int j=1;j<=8;j++){
scanf("%d",&b[i][j]);
}}
for(int i=1;i<=8;i++){
for(int j=1;j<=8;j++){
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+b[i][j];
}}
for(int i=1;i<=8;i++){
for(int j=1;j<=8;j++){
for(int k=i;k<=8;k++){
for(int l=j;l<=8;l++){
d=sum[k][l]-sum[i-1][l]-sum[k][j-1]+sum[i-1][j-1];
dp[i][j][k][l][0]=d*d;
}}}}
for(int num=1;num<n;num++){
for(int i=1;i<=8;i++){
for(int j=1;j<=8;j++){
for(int k=i;k<=8;k++){
for(int l=j;l<=8;l++){
int minn=0x3f3f3f3f;//由于要枚举2次,一次为竖,一次为横,所以用一个minn记录,0x3f3f3f3f ,可视为无穷小
for(int a=j;a<l;a++){
minn=min(minn,min(dp[i][j][k][a][num-1]+dp[i][a+1][k][l][0],dp[i][a+1][k][l][num-1]+dp[i][j][k][a][0]));
}
for(int b=i;b<k;b++){
minn=min(minn,min(dp[b+1][j][k][l][num-1]+dp[i][j][b][l][0],dp[i][j][b][l][num-1]+dp[b+1][j][k][l][0]));
}
dp[i][j][k][l][num]=minn;
}}}}}
printf("%d",dp[1][1][8][8][n-1]);//因为n块只需要n-1次切割
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  dp 洛谷