《算法竞赛进阶指南》0x50动态规划 - 状态压缩DP

牧场的安排
#include <bits/stdc++.h>
using namespace std;

const int MOD = 1e8;
int m, n;
int a[19][19];
int f[19][5000];

bool check(int x, int r) {
	if (x >> 1 & x) return false;			//两块草地有公共边 
	for (int j = n - 1; j >= 0; j --) {		
		if ((x >> j & 1) && !a[r][j]) {		//有土地不适合种草 
			return false;
		} 
	}
	return true;
}

int main() {
	cin >> m >> n;
	
	for (int i = 1; i <= m; i ++) {
		for (int j = n - 1; j >= 0; j --) {
			cin >> a[i][j];
		}
	}
	
	f[0][0] = 1;
	for (int i = 1; i <= m + 1; i ++) {
		for (int j = 0; j < 1 << n; j ++) {
			if (!check(j, i)) continue;	
			for (int k = 0; k < 1 << n; k ++) {
				if (!check(k, i - 1)) continue;
				if (j & k) continue;        //两块草地有公共边
				f[i][j] = (f[i][j] + f[i - 1][k]) % MOD;
			}
		}
	}
	cout << f[m + 1][0];

	return 0;
}
涂抹果酱
#include <bits/stdc++.h>
using namespace std;

const int MOD = 1e6; 
int n, m;					//n行m列 
int k, x, num;				//第k行的输入对应十进制数num 
int f[10009][250];			//f[i][j]表示第i行状态为j的方案数 

int cnt, a[250];			//有cnt个合法的状态,a[j]是第j个合法状态对应的十进制数 
int b[250][5];				//b[j]是第j个合法状态的三进制数 

int main() {
	cin >> n >> m >> k;
	
	for (int j = 0; j < pow(3, m); j ++) {
		a[++ cnt] = j;
		int t = j;		

		for (int k = 0; k < m; k ++) {
			b[cnt][k] = t % 3;
			t /= 3;
			
			if (k > 0 && b[cnt][k] == b[cnt][k - 1]) {		//状态j不合法 
				cnt --;		
				break;
			}
		}
	}
	//当m = 5时,一共只有48个合法的状态 
	
	for (int i = 0; i < m; i ++) cin >> x, num = num * 3 + (x - 1);
	f[0][num] = 1;
	
	for (int i = 1; i < n; i ++) {
		for (int j1 = 1; j1 <= cnt; j1 ++) {				//第i行是第j1个合法方案 
			for (int j2 = 1; j2 <= cnt; j2 ++) {			//第i-1行是第j2个合法方案
				bool flag = true;
				for (int k = 0; k < m; k ++) {
					if (b[j1][k] == b[j2][k]) {
						flag = false;
						break;
					}
				}
				if (flag) {
					f[i][a[j1]] = (f[i][a[j1]] + f[i - 1][a[j2]]) % MOD;
				}
			}
		}
	}
	
	int ans1 = 0, ans2 = 0;	
	for (int j = 1; j <= cnt; j ++) {
		ans1 = (ans1 + f[k - 1][a[j]]) % MOD;				//往上涂抹的方案数 
		ans2 = (ans2 + f[n - k][a[j]]) % MOD;				//往下涂抹的方案数
	}
	
	cout << (long long) ans1 * ans2 % MOD; 
	
	return 0;
}
国王
#include <bits/stdc++.h>
#define LL long long
using namespace std;

int n, m;				//n * n的棋盘,摆放m个国王 
LL f[19][109][1099];	//f[i][j][k]:前i行摆放j个国王,第i行状态为k的合法方案数 
int cnt, st[1099];		//一共有cnt种合法的状态 
int c[1099];			//c[k]表示状态k有几个国王 

int countBit(int k) {
	int res = 0;
	while (k) res ++, k &= k - 1;
	return res;
}

int main() {
	cin >> n >> m;
	
	for (int k = 0; k < 1 << n; k ++) {	//列出m种合法的状态 
		if (!(k >> 1 & k)) {			//没有相邻的国王才是合法状态 
			st[++ cnt] = k;
			c[k] = countBit(k);
		} 
	}
	//当n = 10时,cnt只有144,所以以下代码不会超时,约等于2e7
		
	f[0][0][0] = 1;
	for (int i = 1; i <= n + 1; i ++) {
		for (int j = 0; j <= m; j ++) {
			for (int l2 = 1; l2 <= cnt; l2 ++) {		
				for (int l1 = 1; l1 <= cnt; l1 ++) {
					int k1 = st[l1];					//第i - 1行的状态为k1
					int k2 = st[l2];					//第i行的状态为k2
					if (!(k1 & k2) && !(k1 >> 1 & k2) && !(k1 << 1 & k2)) {
						if (j >= c[k2]) f[i][j][k2] += f[i - 1][j - c[k2]][k1];
						//不必判断j >= c[k1] + c[k2],因为不合法的情况f为0,不影响结果
						//但必须判断j >= c[k2],因为下标不能越界 
					}
				}				
			}
		}
	}
	cout << f[n + 1][m][0];	

	return 0;
}

预处理和每个状态兼容的其它状态

#include <bits/stdc++.h>
#define LL long long
using namespace std;

int n, m;					//n * n的棋盘,摆放m个国王 
LL f[19][109][1099];		//f[i][j][k]:前i行摆放j个国王,第i行状态为k的合法方案数 
vector <int> st;			//st存放所有合法的状态 
vector <int> head[1099];	//head[k]存放所有和状态k相容的状态 
int c[1099];				//c[k]表示状态k有几个国王 

int countBit(int k) {
	int res = 0;
	while (k) res ++, k &= k - 1;
	return res;
}

int main() {
	cin >> n >> m;
	
	for (int k = 0; k < 1 << n; k ++) {	//列出m种合法的状态 
		if (!(k >> 1 & k)) {			//没有相邻的国王才是合法状态 
			st.push_back(k);
			c[k] = countBit(k);
		} 
	}
	
	for (int l2 = 0; l2 < st.size(); l2 ++) {		
		for (int l1 = 0; l1 < st.size(); l1 ++) {
			int k1 = st[l1];					//第i - 1行的状态为k1
			int k2 = st[l2];					//第i行的状态为k2
			if (!(k1 & k2) && !(k1 >> 1 & k2) && !(k1 << 1 & k2)) {
				head[k2].push_back(k1);
			}
		}
	}

	f[0][0][0] = 1;
	for (int i = 1; i <= n + 1; i ++) {
		for (int j = 0; j <= m; j ++) {
			for (int l2 = 0; l2 < st.size(); l2 ++) {		
				int k2 = st[l2];						//第i行的状态为k2
				for (int l1 = 0; l1 < head[k2].size(); l1 ++) {
					int k1 = head[k2][l1];				//第i - 1行的状态为k1
					if (j >= c[k2]) f[i][j][k2] += f[i - 1][j - c[k2]][k1];
						//不必判断j >= c[k1] + c[k2],因为不合法的情况f为0,不影响结果
						//但必须判断j >= c[k2],因为下标不能越界 
				}				
			}
		}
	}
	cout << f[n + 1][m][0];	

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值