小区划分 计蒜客提高组模拟赛(三)Day2 动态规划 区间DP NOIP模拟赛
一条街道的两侧各连续坐落着 N座单元楼。现在要为这些单元楼划分居民校区。
规则如下:
1. 每个小区只能由同一侧连续的若干座单元楼组成。且两侧都恰有 K个小区(每个小区至少有一栋楼)。
2. 两侧的小区划分规则应该相同,比如,若左边的房子被分成 {1,2},{3} 这两个小区,那么右边也应该如此。
这样两边合计一共有 K对小区。
用 ai,bi 表示左右两边每座楼的人口在同侧所有单元楼总人口中所占的百分比,定义一个小区的相对拥挤程度为其人口百分比之和(左边就是对应ai 的和,右边是对应 bi 的和)。定义这条街道的总拥挤程度为左右两边 K对小区的相对拥挤程度之差的绝对值之和。
现在,请你求出可能的最大拥挤程度。
一道简单的动态规划题目。 首先介绍 cmath 库中的一个命令 fabs() , 这玩意儿是专门给浮点数(double)求绝对值的。
我们先把街道两侧的单元楼的人口百分比之和 a、b 做一个前缀和,便于以后的状态转移。设其分别为 suma、sumb
设 dp[i][v] 表示前i座单元楼中划分出v个小区的最大拥挤度,那么显然会先有一个限制条件,即 v≤i 。
转移方程: dp[i][v]=max(dp[i][v],dp[j][v-1]+fabs(suma[i]-suma[j]-(sumb[i]-sumb[j]))) 其中 j∈[v-1,i-1]
初始值就是 dp[1~n][1]=fabs(suma[i]-sumb[i])
也就是说,i的状态是由他之前的合法状态转移过来,i分v个小区,那么就从之前j分的v-1个小区转移过来的方案中取最优方案。
最后输出答案 dp[n][k] 即可。
附上AC代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<algorithm> 3 #include<cmath> 4 using namespace std; 5 template<class T> inline void read(T &_a){ 6 bool f=0;int _ch=getchar();_a=0; 7 while(_ch<'0' || _ch>'9'){if(_ch=='-')f=1;_ch=getchar();} 8 while(_ch>='0' && _ch<='9'){_a=(_a<<1)+(_a<<3)+_ch-'0';_ch=getchar();} 9 if(f)_a=-_a; 10 } 11 12 const int maxn=801; 13 int n,k; 14 double a[maxn],b[maxn],suma[maxn],sumb[maxn],dp[maxn][81]; 15 16 int main() 17 { 18 freopen("dis.in","r",stdin); 19 freopen("dis.out","w",stdout); 20 read(n); read(k); 21 for (register int i=1;i<=n;++i) scanf("%lf",&a[i]); 22 for (register int i=1;i<=n;++i) scanf("%lf",&b[i]); 23 for (register int i=1;i<=n;++i) suma[i]=suma[i-1]+a[i],sumb[i]=sumb[i-1]+b[i]; 24 for (register int i=1;i<=n;++i) 25 { 26 dp[i][1]=fabs(suma[i]-sumb[i]); 27 for (register int v=2;v<=k&&v<=i;++v) 28 for (register int j=i-1;j>=v-1;--j) 29 dp[i][v]=max(dp[i][v],dp[j][v-1]+fabs(suma[i]-suma[j]-(sumb[i]-sumb[j]))); 30 } 31 printf("%lf",dp[n][k]); 32 return 0; 33 }