这题与上面的题目类似,不过限制了可选的总数。令dp[i][j][k]表示前i行,状态为j,共计k个。按照之前的思路,我们选定了状态j后,这一行的“国王”总数就确定为j中二进制1的个数(记为popcnt(j))了,因此前i-1行还要k-popcnt(j)个。
代码如下:
int tot=(n<2)?2:(1<<(n-1))+(1<<(n-2));
for(int i=1; i<=n; i++) {
for(int j=0; j<tot; j++) {
if(j&(j>>1)) continue;
for(int k=0; k<tot; k++) { //上一行
if(!check(j,k)||k&(k>>1)) continue;
int have=popCount(j); //这一行选了几个
for(int t=have; t<=m; t++) { //总计要几个
dp[i][j][t]+=dp[i-1][k][t-have]; //前i-1行还要选几个
}
}
}
}
可以通过预处理所有合法状态并保存到数组中进行优化。
最终代码如下:
int tot=init(n); //合法状态总数
for(int i=1; i<=n; i++) {
for(int j=0; j<tot; j++) {
for(int k=0; k<tot; k++) { //上一行
//时刻记住枚举的只是状态编号,stat中存的才是真实状态
if(!check(stat[j],stat[k])) continue;
int have=popCount(stat[j]); //这一行选了几个
for(int t=have; t<=m; t++) {
dp[i][stat[j]][t]+=dp[i-1][stat[k]][t-have]; //前i-1行还要选几个
}
}
}
}
附:popCount函数可直接使用__builtin_popcount,但某些编译器不支持此函数 因此可以自行实现。下面的实现代码原理是不断去掉最低位的1,比朴素的O(logn)代码平均性能好。(毕竟其他方法也记不住…)
int popCount(unsigned int n) {
int cnt=0;
while(n>0) {
cnt++;
n&=(n-1);
}
return cnt;
}
由于本题每一行都会影响上两行的状态,一个二维数组似乎无法满足我们的要求,因此设dp[i][j][k]为前i行,当前状态为j,上一状态为k。很明显,我们需要4层循环。条件可以类比例题1得出。
因此:
dp[i][S0][S1]=max{dp[i][S1][S2]+popCount(S0)},Si为当前行前i行的状态。
然而本题如果直接暴力做时间复杂度高达
O
(
n
2
3
m
)
O(n2^{3m})
O(n23m),因此必须优化。由于有3层是枚举状态,因此我们可以从以下两个方面入手:
1.减少需要枚举的状态;
2.枚举状态内部进行的某些计算,同一状态要计算多次。可通过预处理,降为只用计算1次。
对于1,我们向上面一样预处理所有合法状态;对于2,我们可以预处理所有合法状态的popcount。
但大量的预处理导致空间可能不足。由于我太弱了,只能发现无论怎么选答案都不会超过SHORT_MAX,最大状态也不超过,因此只会将dp[][][]和sta[]改成short型。
注意要预处理第一行,因为第一行不用考虑前面的行。还应注意只有一行的特殊情况。
//预处理所有状态
int tot=initState(m);
for(int i=0; i<tot; i++) {
pop[sta[i]]=popCnt(sta[i]); //预处理所有popcount
}
//第一行
for(int i=0; i<tot; i++) {
if(checkH(1,sta[i])) {
dp[1][sta[i]][0]=pop[sta[i]];
res=max(res,dp[1][sta[i]][0]); //可能少于2行!
}
}
//永远记住sta[j]代表的是真实状态,而不是j本身!
for(int i=2; i<=n; i++) {
for(int j=0; j<tot; j++) { //this
if(!checkH(i,sta[j])) continue;
short now=sta[j];
for(int k=0; k<tot; k++) { //last
if(!checkH(i-1,sta[k])||(now&sta[k])) continue;
for(int h=0; h<tot; h++) { //last-last
if(!checkH(i-2,sta[h])||(now&sta[h])) continue;
dp[i][now][sta[k]]=max(dp[i][sta[j]][sta[k]],(short)(dp[i-1][sta[k]][sta[h]]+pop[now]));
}
res=max(res,dp[i][sta[j]][sta[k]]);
}
}
}