这个题是想介绍状态压缩。
其实这题的状态压缩并不难,因为需要记录的只是前面位置是否清扫,也即只有0、1两种状态,故使用一个bit位来记录一个位置。
但是这题跟之前做的动归不同的地方是:
之前的题目状态转移方程是f[i, j]可以由以前的状态算出;
这个题状态转移方程是f[i, j]可以推出以后的几个状态的值。
因此这题就需要将当前状态可以导致的所有以后的状态都进行更新。
状态 f[i, j] 表示已经处理到第 i 个位置,j 的二进制形式记录了当前位置 i 前 m – 1 个位置(包括 i )的清扫情况,最低有效位表示当前位置的前第 m – 1 个位置是否清扫,最高有效位表示当前位置是否清扫情况。
转移方程 f[i, j] 可以转移到 f[i + 1, j >> 1],即第 i + 1 位置不清扫;若 j 的二进制表示中 1 的个数小于q,则还可以转移至状态 f[i + 1, (j >> 1) | (1 << (m - 2))],即清扫第 i + 1 位置。
结果的状态表示是:max(f[n, k]) for all valid k。
由于 f[i + 1, j] 只由 f[i, k] 决定,使用滚动数组进行空间压缩。
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
int w[1005];
int f[2][1<<9];
int bits_cnt(int k) {
	int rst = 0;
	while (k) {
		rst += (k & 1);
		k >>= 1;
	}
	return rst;
}
int main() {
	int n, m, q;
	cin >> n >> m >> q;
	
	for (int i = 1; i <= n; i++) {
		cin >> w[i];
	}
	memset(f, -1, sizeof(f));
	f[1][0] = 0;
	f[1][1 << (m - 2)] = w[1];
	int rst = 0;
	for (int i = 1; i < n; i++) {
		for (int j = 0; j < (1 << (m - 1)); j++) {
			if (f[i & 1][j] >= 0) {
				int cnt = bits_cnt(j);
				if (cnt < q) {
					int t = max(f[(i + 1) & 1][(j >> 1) | (1 << (m - 2))], f[i & 1][j] + w[i + 1]);
					f[(i + 1) & 1][(j >> 1) + (1 << (m - 2))] = t;
					rst = max(rst, t);
					f[(i + 1) & 1][j >> 1] = max(f[(i + 1) & 1][j >> 1], f[i & 1][j]);
				}
				else if (cnt == q) {
					f[(i + 1) & 1][j >> 1] = max(f[(i + 1) & 1][j >> 1], f[i & 1][j]);
				}
			}
		}
	}
	cout << rst << endl;
	return 0;
}
吐槽:
“因此这题就需要将当前状态可以导致的所有以后的状态都进行更新。”
 
                    
                     
                    
                 
                    
                 

 posted on
 posted on 
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号