题目描述

这个题是想介绍状态压缩。

其实这题的状态压缩并不难,因为需要记录的只是前面位置是否清扫,也即只有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;
}

 

吐槽:

“因此这题就需要将当前状态可以导致的所有以后的状态都进行更新。”

image

 posted on 2015-04-29 20:32  xblade  阅读(139)  评论(0)    收藏  举报