一道简单易错的动态规划问题
棋盘分割
Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 13687 | Accepted: 4873 |
Description
将一个8*8的棋盘进行如下分割:将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,再将剩下的部分继续如此分割,这样割了(n-1)次后,连同最后剩下的矩形棋盘共有n块矩形棋盘。(每次切割都只能沿着棋盘格子的边进行)
原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。现在需要把棋盘按上述规则分割成n块矩形棋盘,并使各矩形棋盘总分的均方差最小。
均方差 ,其中平均值 ,x i为第i块矩形棋盘的总分。
请编程对给出的棋盘及n,求出O'的最小值。
原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。现在需要把棋盘按上述规则分割成n块矩形棋盘,并使各矩形棋盘总分的均方差最小。
均方差 ,其中平均值 ,x i为第i块矩形棋盘的总分。
请编程对给出的棋盘及n,求出O'的最小值。
Input
第1行为一个整数n(1 < n < 15)。
第2行至第9行每行为8个小于100的非负整数,表示棋盘上相应格子的分值。每行相邻两数之间用一个空格分隔。
第2行至第9行每行为8个小于100的非负整数,表示棋盘上相应格子的分值。每行相邻两数之间用一个空格分隔。
Output
仅一个数,为O'(四舍五入精确到小数点后三位)。
Sample Input
3 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 0 3
Sample Output
1.633
Source
详见注释
/**==========================================
* This is a solution for ACM/ICPC problem
*
* @source£º
* @type:
* @author: wust_ysk
* @blog: http://blog.csdn.net/yskyskyer123
* @email: 2530094312@qq.com
*===========================================*/
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<climits>
#include<cmath>
#include<algorithm>
#define sqr(x) ((x)*(x))
using namespace std;
typedef long long ll;
const int INF =0x3f3f3f3f;
const int maxn= 14 ;
//const int maxV=12 ;
int n;
int a[11][11];//代表第x行,第y列上的数大小
/*
原点坐标为[0][0],[x1][x2][y1][y2]通过了4个坐标描述了一个矩形,其中x轴竖直向下,y轴水平向右
*/
int pre[11][11];//pre[x][y]代表矩形[0][x][0][y]的数和
int dp[maxn+2][10][10][10][10];//[切的次数][x1][x2][y1][y2]的最小差的平方和
int cal(int x1,int x2,int y1,int y2)
{
int tmp= pre[x2][y1]+pre[x1][y2]-pre[x1][y1];
return sqr( pre[x2][y2]-tmp );
}
int DP(int cnt,int x1,int x2,int y1,int y2)
{
if(dp[cnt][x1][x2][y1][y2]!=-1) return dp[cnt][x1][x2][y1][y2];
// if(x2-x1-1+y2-y1-1<cnt) return dp[cnt][x1][x2][y1][y2]=INF;
if(cnt==1) return dp[cnt][x1][x2][y1][y2]=cal(x1,x2,y1,y2);
int &ans=dp[cnt][x1][x2][y1][y2];
ans=INF;
/*INF是一个上限,返回值一定不超过INF,所以INF的选取很讲究,对于返回式int的问题,
最好不要超过INT_MAX/2,因为常见的dp问题都是两个数相加,INF选取不当很容易爆int
*/
for(int k=x1+1;k<x2;k++)
{
if(x2-k-1+y2-y1-1>=cnt-2)/*加上判断条件可以省一点时间,其实也可以不加,也是对的,因为
如果某块大小不够切一定的次数,返回值一定是INF
*/
ans=min(ans, cal(x1,k,y1,y2 )+DP(cnt-1,k,x2,y1,y2) );
if(k-x1-1+y2-y1-1>=cnt-2)
ans=min(ans, DP(cnt-1,x1,k,y1,y2 )+cal(k,x2,y1,y2) );
}
for(int k=y1+1;k<y2;k++)
{
if(x2-x1-1+y2-k-1>=cnt-2)
ans=min(ans, cal(x1,x2,y1,k) +DP(cnt-1,x1,x2 ,k,y2) );
if(x2-x1-1+k-y1-1>=cnt-2)
ans=min(ans, DP(cnt-1,x1,x2,y1,k) +cal(x1,x2 ,k,y2) );
}
return ans;
}
void precope()
{
memset(pre[0],0,sizeof pre[0]);
for(int x=1;x<=8;x++)
{
pre[x][0]=0;
int sum=0;
for(int y=1;y<=8;y++)
{
sum+=a[x][y];
pre[x][y]=pre[x-1][y]+sum;
}
}
}
int main()
{
/*
这个题目的答案是sqrt( ( sum(xi^2)-2*ave(x)*sum(xi)+sum(ave(x)^2) )/n );
动态规划问题求得是最小的 sum(xi^2)
我想说说为什么求的不是:
最小的 sum( (xi-ave(x))^2 )
如果求得是这个,不是很直接,很自然吗?
很简单因为ave(x)是double型变量,dp返回的是int,当然不能这么弄
之前一直wa,看了很多题解,他们都说黑书上dp的意义是 最小的 sum(xi^2),
后来我反复看代码,发现ave(x) 是double变量 是wa的根本原因。
这个scanf("%d",&n) 加不加!EOF 都可以AC,
题目里面并没有明确说有多组数据。(肯定有的)
*/
scanf("%d",&n);
double sum=0;
for(int x=1;x<=8;x++)
{
for(int y=1;y<=8;y++)
{
scanf("%d",&a[x][y]);
sum+=a[x][y];
}
}
double ave=sum/n;
precope();
memset(dp,-1,sizeof dp);
double tmp=DP(n,0,8,0,8);
tmp=tmp-2*ave*sum+ n*sqr(ave);
tmp/=n;
tmp=sqrt(tmp);
printf("%.3f\n",tmp);
/*
注释写了这么多,都是做题时思考到的,很容易错的地方,这个题告诉我们细节很重要:
变量的类型导致的精度问题,变量的范围导致的爆int问题...etc,
有时还会少些一个~导致TLE的问题,无穷大INF设置是否合理...etc...
变量是否有初始化,边界条件是否考虑到,有没有进行特判?
精度问题有没有考虑到?有时侯输入的%d写成了%lld直接成了wa。
有时侯wa往往离ac只有一步之遥,却总在关键时刻想不到。
%lld?%I64d
是取最大还是最小想清楚没?
这上下两行代码换了个位置就对了,你想清区别没有?
*/
return 0;
}